@openzeppelin/ui-react 3.0.1 → 3.2.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 +7 -0
- package/dist/index.cjs +678 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +257 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +257 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +668 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -29,10 +29,11 @@ let react = require("react");
|
|
|
29
29
|
react = __toESM(react);
|
|
30
30
|
let _openzeppelin_ui_utils = require("@openzeppelin/ui-utils");
|
|
31
31
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
32
|
+
let _tanstack_react_query = require("@tanstack/react-query");
|
|
32
33
|
let _openzeppelin_ui_components = require("@openzeppelin/ui-components");
|
|
33
34
|
|
|
34
35
|
//#region src/version.ts
|
|
35
|
-
const VERSION = "3.0
|
|
36
|
+
const VERSION = "3.2.0";
|
|
36
37
|
|
|
37
38
|
//#endregion
|
|
38
39
|
//#region src/hooks/AdapterContext.tsx
|
|
@@ -85,11 +86,31 @@ function RuntimeProvider({ children, resolveRuntime }) {
|
|
|
85
86
|
const [loadingNetworks, setLoadingNetworks] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
86
87
|
const runtimeRegistryRef = (0, react.useRef)(runtimeRegistry);
|
|
87
88
|
const failedNetworksRef = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
89
|
+
const resolverGenerationRef = (0, react.useRef)(0);
|
|
90
|
+
const resolverInitializedRef = (0, react.useRef)(false);
|
|
88
91
|
(0, react.useEffect)(() => {
|
|
89
92
|
runtimeRegistryRef.current = runtimeRegistry;
|
|
90
93
|
}, [runtimeRegistry]);
|
|
91
94
|
(0, react.useEffect)(() => {
|
|
95
|
+
if (resolverInitializedRef.current) resolverGenerationRef.current += 1;
|
|
96
|
+
else resolverInitializedRef.current = true;
|
|
97
|
+
const registry = runtimeRegistryRef.current;
|
|
98
|
+
const runtimes = Object.values(registry);
|
|
99
|
+
const hadCachedRuntimes = runtimes.length > 0;
|
|
92
100
|
failedNetworksRef.current = /* @__PURE__ */ new Set();
|
|
101
|
+
setLoadingNetworks(/* @__PURE__ */ new Set());
|
|
102
|
+
if (hadCachedRuntimes) {
|
|
103
|
+
setRuntimeRegistry({});
|
|
104
|
+
setTimeout(() => {
|
|
105
|
+
runtimes.forEach((runtime) => {
|
|
106
|
+
try {
|
|
107
|
+
runtime.dispose();
|
|
108
|
+
} catch (error) {
|
|
109
|
+
_openzeppelin_ui_utils.logger.error("RuntimeProvider", "Error disposing runtime during registry flush:", error);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}, 0);
|
|
113
|
+
}
|
|
93
114
|
}, [resolveRuntime]);
|
|
94
115
|
(0, react.useEffect)(() => {
|
|
95
116
|
return () => {
|
|
@@ -177,7 +198,23 @@ function RuntimeProvider({ children, resolveRuntime }) {
|
|
|
177
198
|
return newSet;
|
|
178
199
|
});
|
|
179
200
|
_openzeppelin_ui_utils.logger.info("RuntimeProvider", `Starting runtime initialization for network ${networkId} (${networkConfig.name})`);
|
|
201
|
+
const generation = resolverGenerationRef.current;
|
|
180
202
|
resolveRuntime(networkConfig).then((runtime) => {
|
|
203
|
+
if (generation !== resolverGenerationRef.current) {
|
|
204
|
+
setTimeout(() => {
|
|
205
|
+
try {
|
|
206
|
+
runtime.dispose();
|
|
207
|
+
} catch (error) {
|
|
208
|
+
_openzeppelin_ui_utils.logger.error("RuntimeProvider", `Error disposing stale runtime for network ${networkId}:`, error);
|
|
209
|
+
}
|
|
210
|
+
}, 0);
|
|
211
|
+
setLoadingNetworks((prev) => {
|
|
212
|
+
const newSet = new Set(prev);
|
|
213
|
+
newSet.delete(networkId);
|
|
214
|
+
return newSet;
|
|
215
|
+
});
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
181
218
|
_openzeppelin_ui_utils.logger.info("RuntimeProvider", `Runtime for network ${networkId} loaded successfully`, {
|
|
182
219
|
type: runtime.constructor.name,
|
|
183
220
|
objectId: Object.prototype.toString.call(runtime)
|
|
@@ -192,6 +229,14 @@ function RuntimeProvider({ children, resolveRuntime }) {
|
|
|
192
229
|
return newSet;
|
|
193
230
|
});
|
|
194
231
|
}).catch((error) => {
|
|
232
|
+
if (generation !== resolverGenerationRef.current) {
|
|
233
|
+
setLoadingNetworks((prev) => {
|
|
234
|
+
const newSet = new Set(prev);
|
|
235
|
+
newSet.delete(networkId);
|
|
236
|
+
return newSet;
|
|
237
|
+
});
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
195
240
|
_openzeppelin_ui_utils.logger.error("RuntimeProvider", `Error loading runtime for network ${networkId}:`, error);
|
|
196
241
|
failedNetworksRef.current.add(networkId);
|
|
197
242
|
setLoadingNetworks((prev) => {
|
|
@@ -272,12 +317,12 @@ const WALLET_STATE_CONTEXT_KEY = Symbol.for("@openzeppelin/ui-react/WalletStateC
|
|
|
272
317
|
* 4. For React contexts specifically, having the same context object is what matters,
|
|
273
318
|
* and this pattern guarantees that after initialization
|
|
274
319
|
*/
|
|
275
|
-
function getOrCreateSharedContext() {
|
|
320
|
+
function getOrCreateSharedContext$1() {
|
|
276
321
|
const global = globalThis;
|
|
277
322
|
if (!global[WALLET_STATE_CONTEXT_KEY]) global[WALLET_STATE_CONTEXT_KEY] = (0, react.createContext)(void 0);
|
|
278
323
|
return global[WALLET_STATE_CONTEXT_KEY];
|
|
279
324
|
}
|
|
280
|
-
const WalletStateContext = getOrCreateSharedContext();
|
|
325
|
+
const WalletStateContext = getOrCreateSharedContext$1();
|
|
281
326
|
/**
|
|
282
327
|
* Hook to access wallet state from WalletStateProvider.
|
|
283
328
|
* @throws Error if used outside of WalletStateProvider
|
|
@@ -905,6 +950,625 @@ function useWalletReconnectionHandler(selectedNetworkConfigId, selectedCapabilit
|
|
|
905
950
|
]);
|
|
906
951
|
}
|
|
907
952
|
|
|
953
|
+
//#endregion
|
|
954
|
+
//#region src/hooks/nameResolution/resolutionConfig.ts
|
|
955
|
+
/**
|
|
956
|
+
* Default resolution config. Values sit within the spec's "seconds-to-minutes"
|
|
957
|
+
* caching guidance; forward debounce is 300ms (typed char-by-char), reverse is 0.
|
|
958
|
+
*/
|
|
959
|
+
const DEFAULT_CONFIG = {
|
|
960
|
+
staleTimeMs: 6e4,
|
|
961
|
+
gcTimeMs: 3e5,
|
|
962
|
+
forwardDebounceMs: 300,
|
|
963
|
+
reverseDebounceMs: 0,
|
|
964
|
+
transientRetryCount: 2
|
|
965
|
+
};
|
|
966
|
+
/** Stable prefix for every resolution cache key — namespaces this package's keys. */
|
|
967
|
+
const RESOLUTION_KEY_PREFIX = "oz-name-resolution";
|
|
968
|
+
const runtimeInstanceIds = /* @__PURE__ */ new WeakMap();
|
|
969
|
+
let nextRuntimeInstanceId = 1;
|
|
970
|
+
/**
|
|
971
|
+
* Assign a stable, serializable id to each {@link EcosystemRuntime} object identity.
|
|
972
|
+
* A disposed-and-recreated runtime (e.g. SF-4 opt-in toggle after INV-218 flush) gets
|
|
973
|
+
* a new id so resolution cache keys miss and results refresh (INV-230).
|
|
974
|
+
*/
|
|
975
|
+
function getRuntimeInstanceId(runtime) {
|
|
976
|
+
if (!runtime) return 0;
|
|
977
|
+
let id = runtimeInstanceIds.get(runtime);
|
|
978
|
+
if (id === void 0) {
|
|
979
|
+
id = nextRuntimeInstanceId;
|
|
980
|
+
nextRuntimeInstanceId += 1;
|
|
981
|
+
runtimeInstanceIds.set(runtime, id);
|
|
982
|
+
}
|
|
983
|
+
return id;
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Build a network-scoped, namespace-separated cache key (INV-40). A name resolves
|
|
987
|
+
* differently per network and provenance can be chain-scoped, so `networkId` is
|
|
988
|
+
* part of the key; `namespace` keeps forward and reverse lookups disjoint;
|
|
989
|
+
* `runtimeInstanceId` scopes entries to the active capability instance (INV-230).
|
|
990
|
+
*
|
|
991
|
+
* @param namespace - `'name'` (forward) or `'addr'` (reverse).
|
|
992
|
+
* @param networkId - Active network id (`''` when no network is selected).
|
|
993
|
+
* @param normalizedInput - Trimmed + lowercased input (INV-26).
|
|
994
|
+
* @param runtimeInstanceId - Active runtime instance discriminator (`0` when absent).
|
|
995
|
+
* @returns A readonly tuple suitable for a TanStack Query `queryKey`.
|
|
996
|
+
*/
|
|
997
|
+
function buildResolutionKey(namespace, networkId, normalizedInput, runtimeInstanceId = 0) {
|
|
998
|
+
return [
|
|
999
|
+
RESOLUTION_KEY_PREFIX,
|
|
1000
|
+
namespace,
|
|
1001
|
+
networkId,
|
|
1002
|
+
normalizedInput,
|
|
1003
|
+
runtimeInstanceId
|
|
1004
|
+
];
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Whether a resolution error is transient (worth retrying) versus a definitive,
|
|
1008
|
+
* stable answer (INV-47). Exhaustive over SF-1's closed error union with no
|
|
1009
|
+
* `default`: adding a new error code to `@openzeppelin/ui-types` will fail to
|
|
1010
|
+
* compile here until it is classified — deliberately coupling SF-2 to SF-1 INV-7.
|
|
1011
|
+
*
|
|
1012
|
+
* @param error - A typed {@link NameResolutionError}.
|
|
1013
|
+
* @returns `true` for transient failures (timeout / gateway / adapter), `false`
|
|
1014
|
+
* for definitive negatives and unsupported-network answers.
|
|
1015
|
+
*/
|
|
1016
|
+
function isTransientError(error) {
|
|
1017
|
+
switch (error.code) {
|
|
1018
|
+
case "RESOLUTION_TIMEOUT":
|
|
1019
|
+
case "EXTERNAL_GATEWAY_ERROR":
|
|
1020
|
+
case "ADAPTER_ERROR": return true;
|
|
1021
|
+
case "NAME_NOT_FOUND":
|
|
1022
|
+
case "ADDRESS_NOT_FOUND":
|
|
1023
|
+
case "UNSUPPORTED_NAME":
|
|
1024
|
+
case "UNSUPPORTED_NETWORK": return false;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Construct a QueryClient tuned for name resolution: ambient refetch triggers are
|
|
1029
|
+
* disabled at the client level (INV-41) so a resolved value never silently
|
|
1030
|
+
* changes under the user on window focus or network reconnect.
|
|
1031
|
+
*/
|
|
1032
|
+
function createResolutionQueryClient() {
|
|
1033
|
+
return new _tanstack_react_query.QueryClient({ defaultOptions: { queries: {
|
|
1034
|
+
refetchOnWindowFocus: false,
|
|
1035
|
+
refetchOnReconnect: false
|
|
1036
|
+
} } });
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Process-global key for the default resolution QueryClient. Uses `Symbol.for`
|
|
1040
|
+
* so every duplicated module instance (Vite pre-bundling, npm-installed adapters)
|
|
1041
|
+
* shares ONE client — the same cross-bundle-singleton pattern as
|
|
1042
|
+
* `WalletStateContext`. This is what makes the cache shared with zero wiring
|
|
1043
|
+
* (SC-001) and enables cross-instance dedup / warm-cache reuse (INV-33 / INV-37 / INV-48).
|
|
1044
|
+
*/
|
|
1045
|
+
const DEFAULT_CLIENT_KEY = Symbol.for("@openzeppelin/ui-react/NameResolutionQueryClient");
|
|
1046
|
+
/**
|
|
1047
|
+
* Lazily create (exactly once) and return the process-global resolution
|
|
1048
|
+
* QueryClient used when no `NameResolutionProvider` is mounted (INV-48).
|
|
1049
|
+
*
|
|
1050
|
+
* @returns The shared default resolution QueryClient (stable across calls).
|
|
1051
|
+
*/
|
|
1052
|
+
function getDefaultResolutionQueryClient() {
|
|
1053
|
+
const globalScope = globalThis;
|
|
1054
|
+
if (!globalScope[DEFAULT_CLIENT_KEY]) globalScope[DEFAULT_CLIENT_KEY] = createResolutionQueryClient();
|
|
1055
|
+
return globalScope[DEFAULT_CLIENT_KEY];
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
//#endregion
|
|
1059
|
+
//#region src/hooks/nameResolution/NameResolutionContext.ts
|
|
1060
|
+
/**
|
|
1061
|
+
* Process-global key for the context object. Uses `Symbol.for` so all duplicated
|
|
1062
|
+
* module instances share ONE React context — the same cross-bundle-singleton
|
|
1063
|
+
* pattern as `WalletStateContext` (bundler pre-bundling / npm-installed adapters).
|
|
1064
|
+
*/
|
|
1065
|
+
const CONTEXT_KEY = Symbol.for("@openzeppelin/ui-react/NameResolutionContext");
|
|
1066
|
+
function getOrCreateSharedContext() {
|
|
1067
|
+
const globalScope = globalThis;
|
|
1068
|
+
if (!globalScope[CONTEXT_KEY]) globalScope[CONTEXT_KEY] = (0, react.createContext)(null);
|
|
1069
|
+
return globalScope[CONTEXT_KEY];
|
|
1070
|
+
}
|
|
1071
|
+
/**
|
|
1072
|
+
* React context for resolution. `null` default triggers the module-singleton
|
|
1073
|
+
* fallback in {@link useNameResolutionContext} — no Provider is required (INV-48).
|
|
1074
|
+
*/
|
|
1075
|
+
const NameResolutionContext = getOrCreateSharedContext();
|
|
1076
|
+
/**
|
|
1077
|
+
* Stable fallback value used when no Provider is mounted. Cached per module so the
|
|
1078
|
+
* returned reference is referentially stable across renders (INV-38); it wraps the
|
|
1079
|
+
* process-global default client, so caches are still shared even across bundles.
|
|
1080
|
+
*/
|
|
1081
|
+
let fallbackValue;
|
|
1082
|
+
function getFallbackContextValue() {
|
|
1083
|
+
if (!fallbackValue) fallbackValue = {
|
|
1084
|
+
queryClient: getDefaultResolutionQueryClient(),
|
|
1085
|
+
config: DEFAULT_CONFIG
|
|
1086
|
+
};
|
|
1087
|
+
return fallbackValue;
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Read the resolution context, falling back to the zero-wiring default (global
|
|
1091
|
+
* singleton client + {@link DEFAULT_CONFIG}) when no `NameResolutionProvider` is
|
|
1092
|
+
* mounted (INV-48). Never throws for a missing provider.
|
|
1093
|
+
*
|
|
1094
|
+
* @returns The active {@link NameResolutionContextValue}.
|
|
1095
|
+
*/
|
|
1096
|
+
function useNameResolutionContext() {
|
|
1097
|
+
return (0, react.useContext)(NameResolutionContext) ?? getFallbackContextValue();
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
//#endregion
|
|
1101
|
+
//#region src/hooks/nameResolution/resolutionState.ts
|
|
1102
|
+
/**
|
|
1103
|
+
* Internal error used to bridge SF-1's `ok: false` results (and defensively-caught
|
|
1104
|
+
* adapter throws) into TanStack Query's thrown-error channel, carrying the typed
|
|
1105
|
+
* {@link NameResolutionError} through so the mapping step can surface it unchanged
|
|
1106
|
+
* (INV-43). Never leaks past the hook boundary.
|
|
1107
|
+
*/
|
|
1108
|
+
var ResolutionQueryError = class extends Error {
|
|
1109
|
+
resolutionError;
|
|
1110
|
+
/** @param resolutionError - The typed error to carry through react-query. */
|
|
1111
|
+
constructor(resolutionError) {
|
|
1112
|
+
super(`name resolution failed: ${resolutionError.code}`);
|
|
1113
|
+
this.name = "ResolutionQueryError";
|
|
1114
|
+
this.resolutionError = resolutionError;
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
/**
|
|
1118
|
+
* Convert any value from react-query's error channel into a typed, closed-union
|
|
1119
|
+
* {@link NameResolutionError} (INV-43). A {@link ResolutionQueryError} is
|
|
1120
|
+
* unwrapped verbatim; anything else (a react-query internal error) is mapped to
|
|
1121
|
+
* `ADAPTER_ERROR` so no untyped throw reaches a component.
|
|
1122
|
+
*
|
|
1123
|
+
* @param error - The raw `query.error` value (`unknown`).
|
|
1124
|
+
* @returns A typed error drawn only from SF-1's closed union.
|
|
1125
|
+
*/
|
|
1126
|
+
function toNameResolutionError(error) {
|
|
1127
|
+
if (error instanceof ResolutionQueryError) return error.resolutionError;
|
|
1128
|
+
return {
|
|
1129
|
+
code: "ADAPTER_ERROR",
|
|
1130
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1131
|
+
cause: error
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Map a settled query's state to exactly one non-gate {@link EngineResult} arm
|
|
1136
|
+
* (INV-42, INV-44). The `input` echoed here is the debounced value the query was
|
|
1137
|
+
* keyed on, so `data`/`error` are always paired with the input that produced them
|
|
1138
|
+
* (INV-24). Success without data degrades to `loading` rather than a resolved arm
|
|
1139
|
+
* missing its payload.
|
|
1140
|
+
*
|
|
1141
|
+
* @param input - The debounced, normalized input keying this query.
|
|
1142
|
+
* @param query - The settled query state.
|
|
1143
|
+
* @param retry - Bound `refetch` for the `error` arm (INV-34).
|
|
1144
|
+
* @returns The `resolved`, `error`, or `loading` arm.
|
|
1145
|
+
*/
|
|
1146
|
+
function mapSettledQuery(input, query, retry) {
|
|
1147
|
+
if (query.isSuccess && query.data !== void 0) return {
|
|
1148
|
+
status: "resolved",
|
|
1149
|
+
input,
|
|
1150
|
+
data: query.data
|
|
1151
|
+
};
|
|
1152
|
+
if (query.isError) return {
|
|
1153
|
+
status: "error",
|
|
1154
|
+
input,
|
|
1155
|
+
error: toNameResolutionError(query.error),
|
|
1156
|
+
retry
|
|
1157
|
+
};
|
|
1158
|
+
return {
|
|
1159
|
+
status: "loading",
|
|
1160
|
+
input
|
|
1161
|
+
};
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
//#endregion
|
|
1165
|
+
//#region src/hooks/nameResolution/useResolutionEngine.ts
|
|
1166
|
+
const LOG_SCOPE = "useResolutionEngine";
|
|
1167
|
+
/** No-op retry for synthesized errors that never entered the query path (INV-45). */
|
|
1168
|
+
const NOOP_RETRY = () => {};
|
|
1169
|
+
/**
|
|
1170
|
+
* Shared engine behind `useResolveName` / `useResolveAddress`. Owns input
|
|
1171
|
+
* normalization, debouncing, the `useQuery` wiring (explicit-client form), the
|
|
1172
|
+
* capability-absence gate, and the mapping to a direction-agnostic
|
|
1173
|
+
* {@link EngineResult}. Not exported from the package.
|
|
1174
|
+
*/
|
|
1175
|
+
function useResolutionEngine(params) {
|
|
1176
|
+
const { input, namespace, debounceMs, enabled, shouldAttempt, getMethod } = params;
|
|
1177
|
+
const walletState = (0, react.useContext)(WalletStateContext);
|
|
1178
|
+
const activeRuntime = walletState?.activeRuntime ?? null;
|
|
1179
|
+
const activeNetworkId = walletState?.activeNetworkId ?? null;
|
|
1180
|
+
const isRuntimeLoading = walletState?.isRuntimeLoading ?? false;
|
|
1181
|
+
const { queryClient, config } = useNameResolutionContext();
|
|
1182
|
+
const trimmedLive = (input ?? "").trim();
|
|
1183
|
+
const normalizedLive = trimmedLive.toLowerCase();
|
|
1184
|
+
const [debouncedTrimmed, setDebouncedTrimmed] = (0, react.useState)(trimmedLive);
|
|
1185
|
+
const seededRef = (0, react.useRef)(false);
|
|
1186
|
+
(0, react.useEffect)(() => {
|
|
1187
|
+
if (!seededRef.current) {
|
|
1188
|
+
seededRef.current = true;
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
if (debounceMs <= 0) {
|
|
1192
|
+
setDebouncedTrimmed(trimmedLive);
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
const timer = setTimeout(() => setDebouncedTrimmed(trimmedLive), debounceMs);
|
|
1196
|
+
return () => clearTimeout(timer);
|
|
1197
|
+
}, [trimmedLive, debounceMs]);
|
|
1198
|
+
const effectiveTrimmed = debounceMs <= 0 ? trimmedLive : debouncedTrimmed;
|
|
1199
|
+
const normalizedKey = effectiveTrimmed.toLowerCase();
|
|
1200
|
+
const capability = activeRuntime?.nameResolution;
|
|
1201
|
+
const networkId = activeNetworkId ?? "";
|
|
1202
|
+
const runtimeInstanceId = getRuntimeInstanceId(activeRuntime);
|
|
1203
|
+
const method = capability ? getMethod(capability) : void 0;
|
|
1204
|
+
const liveAttemptable = capability ? shouldAttempt(capability, normalizedLive) : false;
|
|
1205
|
+
const keyAttemptable = capability ? shouldAttempt(capability, normalizedKey) : false;
|
|
1206
|
+
const queryEnabled = enabled && trimmedLive !== "" && capability != null && method != null && normalizedKey !== "" && keyAttemptable;
|
|
1207
|
+
const query = (0, _tanstack_react_query.useQuery)({
|
|
1208
|
+
queryKey: buildResolutionKey(namespace, networkId, normalizedKey, runtimeInstanceId),
|
|
1209
|
+
queryFn: async () => {
|
|
1210
|
+
if (!method) throw new ResolutionQueryError({
|
|
1211
|
+
code: "ADAPTER_ERROR",
|
|
1212
|
+
message: "resolution method unexpectedly missing"
|
|
1213
|
+
});
|
|
1214
|
+
const adapterArg = namespace === "addr" ? effectiveTrimmed : normalizedKey;
|
|
1215
|
+
let result;
|
|
1216
|
+
try {
|
|
1217
|
+
result = await method(adapterArg);
|
|
1218
|
+
} catch (cause) {
|
|
1219
|
+
_openzeppelin_ui_utils.logger.warn(LOG_SCOPE, "resolution method threw instead of returning ok:false", cause);
|
|
1220
|
+
throw new ResolutionQueryError({
|
|
1221
|
+
code: "ADAPTER_ERROR",
|
|
1222
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
1223
|
+
cause
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
if (!result.ok) throw new ResolutionQueryError(result.error);
|
|
1227
|
+
return result.value;
|
|
1228
|
+
},
|
|
1229
|
+
enabled: queryEnabled,
|
|
1230
|
+
staleTime: config.staleTimeMs,
|
|
1231
|
+
gcTime: config.gcTimeMs,
|
|
1232
|
+
refetchOnWindowFocus: false,
|
|
1233
|
+
refetchOnReconnect: false,
|
|
1234
|
+
retry: (failureCount, error) => (error instanceof ResolutionQueryError ? isTransientError(error.resolutionError) : true) && failureCount < config.transientRetryCount
|
|
1235
|
+
}, queryClient);
|
|
1236
|
+
if (!enabled) return { status: "idle" };
|
|
1237
|
+
if (trimmedLive === "") return { status: "idle" };
|
|
1238
|
+
if (capability) {
|
|
1239
|
+
if (!liveAttemptable) return { status: "idle" };
|
|
1240
|
+
if (!method) return unsupportedNetwork(normalizedLive, networkId);
|
|
1241
|
+
if (normalizedKey !== normalizedLive) return {
|
|
1242
|
+
status: "debouncing",
|
|
1243
|
+
input: normalizedLive
|
|
1244
|
+
};
|
|
1245
|
+
} else {
|
|
1246
|
+
if (isRuntimeLoading) return {
|
|
1247
|
+
status: "loading",
|
|
1248
|
+
input: normalizedLive
|
|
1249
|
+
};
|
|
1250
|
+
return unsupportedNetwork(normalizedLive, networkId);
|
|
1251
|
+
}
|
|
1252
|
+
const retry = () => {
|
|
1253
|
+
query.refetch();
|
|
1254
|
+
};
|
|
1255
|
+
return mapSettledQuery(normalizedKey, query, retry);
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Synthesize an `UNSUPPORTED_NETWORK` error arm without touching react-query
|
|
1259
|
+
* (INV-45). Drawn from SF-1's closed union so it is indistinguishable from an
|
|
1260
|
+
* adapter-returned `UNSUPPORTED_NETWORK`; `networkId` may be `''` (INV-49). Retry
|
|
1261
|
+
* is a no-op — there is no query to refetch.
|
|
1262
|
+
*/
|
|
1263
|
+
function unsupportedNetwork(input, networkId) {
|
|
1264
|
+
return {
|
|
1265
|
+
status: "error",
|
|
1266
|
+
input,
|
|
1267
|
+
error: {
|
|
1268
|
+
code: "UNSUPPORTED_NETWORK",
|
|
1269
|
+
networkId
|
|
1270
|
+
},
|
|
1271
|
+
retry: NOOP_RETRY
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
//#endregion
|
|
1276
|
+
//#region src/hooks/nameResolution/useResolveName.ts
|
|
1277
|
+
/**
|
|
1278
|
+
* Forward-resolve a name to an address. Soft-reads the active runtime from
|
|
1279
|
+
* `WalletStateContext` (never throws when no provider is mounted — degrades to
|
|
1280
|
+
* idle / UNSUPPORTED_NETWORK) and calls `runtime.nameResolution?.resolveName`.
|
|
1281
|
+
* Debounces `name`, caches per (network, name) via the owned QueryClient, and is
|
|
1282
|
+
* protected against out-of-order responses (distinct inputs → distinct query keys).
|
|
1283
|
+
*
|
|
1284
|
+
* Returns a discriminated union keyed on `status`; it never throws for expected
|
|
1285
|
+
* failures (they surface in the `error` arm) and never pairs a name with a
|
|
1286
|
+
* different name's resolved address (INV-24).
|
|
1287
|
+
*
|
|
1288
|
+
* @param name - The name to resolve. `null` / empty / a value failing the
|
|
1289
|
+
* adapter's `isValidName` yields `status: 'idle'` (never `error`).
|
|
1290
|
+
* @param options - `debounceMs` / `enabled` overrides.
|
|
1291
|
+
* @returns The current {@link UseResolveNameResult}.
|
|
1292
|
+
*/
|
|
1293
|
+
function useResolveName(name, options) {
|
|
1294
|
+
const { config } = useNameResolutionContext();
|
|
1295
|
+
return toNameResult(useResolutionEngine({
|
|
1296
|
+
input: name,
|
|
1297
|
+
namespace: "name",
|
|
1298
|
+
debounceMs: options?.debounceMs ?? config.forwardDebounceMs,
|
|
1299
|
+
enabled: options?.enabled ?? true,
|
|
1300
|
+
shouldAttempt: (cap, normalized) => cap.isValidName(normalized),
|
|
1301
|
+
getMethod: (cap) => cap.resolveName
|
|
1302
|
+
}));
|
|
1303
|
+
}
|
|
1304
|
+
/**
|
|
1305
|
+
* Remap the engine's generic `input` to `name` (INV-24: keyed off the debounced
|
|
1306
|
+
* input carried in the engine result, not the live prop). Exhaustive over the
|
|
1307
|
+
* union so a new status cannot silently fall through (INV-23 / INV-42).
|
|
1308
|
+
*/
|
|
1309
|
+
function toNameResult(engine) {
|
|
1310
|
+
switch (engine.status) {
|
|
1311
|
+
case "idle": return { status: "idle" };
|
|
1312
|
+
case "debouncing": return {
|
|
1313
|
+
status: "debouncing",
|
|
1314
|
+
name: engine.input
|
|
1315
|
+
};
|
|
1316
|
+
case "loading": return {
|
|
1317
|
+
status: "loading",
|
|
1318
|
+
name: engine.input
|
|
1319
|
+
};
|
|
1320
|
+
case "resolved": return {
|
|
1321
|
+
status: "resolved",
|
|
1322
|
+
name: engine.input,
|
|
1323
|
+
data: engine.data
|
|
1324
|
+
};
|
|
1325
|
+
case "error": return {
|
|
1326
|
+
status: "error",
|
|
1327
|
+
name: engine.input,
|
|
1328
|
+
error: engine.error,
|
|
1329
|
+
retry: engine.retry
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
//#endregion
|
|
1335
|
+
//#region src/hooks/nameResolution/useResolveAddress.ts
|
|
1336
|
+
/**
|
|
1337
|
+
* Reverse-resolve an address to a name. Soft-reads the active runtime from
|
|
1338
|
+
* `WalletStateContext` (never throws when no provider is mounted — degrades to
|
|
1339
|
+
* idle / UNSUPPORTED_NETWORK) and calls `runtime.nameResolution?.resolveAddress`.
|
|
1340
|
+
* Caches per (network, address) via the owned QueryClient. Not debounced by
|
|
1341
|
+
* default — addresses are pasted, not typed char-by-char.
|
|
1342
|
+
*
|
|
1343
|
+
* Applies no client-side address-shape check (INV-32): resolution is attempted on
|
|
1344
|
+
* any non-empty input and malformed addresses are rejected via the adapter's typed
|
|
1345
|
+
* error union. Address-shape validation is SF-3's concern.
|
|
1346
|
+
*
|
|
1347
|
+
* @param address - The address to reverse-resolve. `null` / empty yields
|
|
1348
|
+
* `status: 'idle'`.
|
|
1349
|
+
* @param options - `debounceMs` / `enabled` overrides.
|
|
1350
|
+
* @returns The current {@link UseResolveAddressResult}.
|
|
1351
|
+
*/
|
|
1352
|
+
function useResolveAddress(address, options) {
|
|
1353
|
+
const { config } = useNameResolutionContext();
|
|
1354
|
+
return toAddressResult(useResolutionEngine({
|
|
1355
|
+
input: address,
|
|
1356
|
+
namespace: "addr",
|
|
1357
|
+
debounceMs: options?.debounceMs ?? config.reverseDebounceMs,
|
|
1358
|
+
enabled: options?.enabled ?? true,
|
|
1359
|
+
shouldAttempt: () => true,
|
|
1360
|
+
getMethod: (cap) => cap.resolveAddress
|
|
1361
|
+
}));
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Remap the engine's generic `input` to `address` (INV-24). A `debouncing` arm —
|
|
1365
|
+
* only reachable when a caller passes a non-zero reverse `debounceMs` — collapses
|
|
1366
|
+
* to `loading` so {@link UseResolveAddressResult} keeps no `debouncing` variant.
|
|
1367
|
+
*/
|
|
1368
|
+
function toAddressResult(engine) {
|
|
1369
|
+
switch (engine.status) {
|
|
1370
|
+
case "idle": return { status: "idle" };
|
|
1371
|
+
case "debouncing":
|
|
1372
|
+
case "loading": return {
|
|
1373
|
+
status: "loading",
|
|
1374
|
+
address: engine.input
|
|
1375
|
+
};
|
|
1376
|
+
case "resolved": return {
|
|
1377
|
+
status: "resolved",
|
|
1378
|
+
address: engine.input,
|
|
1379
|
+
data: engine.data
|
|
1380
|
+
};
|
|
1381
|
+
case "error": return {
|
|
1382
|
+
status: "error",
|
|
1383
|
+
address: engine.input,
|
|
1384
|
+
error: engine.error,
|
|
1385
|
+
retry: engine.retry
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
//#endregion
|
|
1391
|
+
//#region src/hooks/nameResolution/NameResolutionProvider.tsx
|
|
1392
|
+
/**
|
|
1393
|
+
* Optional provider that overrides resolution config and/or the QueryClient for
|
|
1394
|
+
* its subtree. It does NOT render a `QueryClientProvider`: the client is passed
|
|
1395
|
+
* explicitly to `useQuery` (INV-48), so no ambient react-query provider is needed.
|
|
1396
|
+
*
|
|
1397
|
+
* Config is merged field-by-field over {@link DEFAULT_CONFIG} (INV-30), and the
|
|
1398
|
+
* context value is memoized so a Provider re-render does not cascade to every hook
|
|
1399
|
+
* (INV-38).
|
|
1400
|
+
*/
|
|
1401
|
+
function NameResolutionProvider({ children, config, queryClient }) {
|
|
1402
|
+
const value = (0, react.useMemo)(() => ({
|
|
1403
|
+
queryClient: queryClient ?? getDefaultResolutionQueryClient(),
|
|
1404
|
+
config: {
|
|
1405
|
+
...DEFAULT_CONFIG,
|
|
1406
|
+
...config
|
|
1407
|
+
}
|
|
1408
|
+
}), [queryClient, config]);
|
|
1409
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NameResolutionContext.Provider, {
|
|
1410
|
+
value,
|
|
1411
|
+
children
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
//#endregion
|
|
1416
|
+
//#region src/hooks/nameResolution/useRuntimeNameResolver.ts
|
|
1417
|
+
/**
|
|
1418
|
+
* Stable empty resolver returned when no runtime / capability is available.
|
|
1419
|
+
* No methods ⇒ the consuming field treats a typed name as unsupported and
|
|
1420
|
+
* surfaces `UNSUPPORTED_NETWORK` (INV-119) while hex behavior stays legacy.
|
|
1421
|
+
*/
|
|
1422
|
+
const EMPTY_RESOLVER = {};
|
|
1423
|
+
/**
|
|
1424
|
+
* Project the active runtime's `NameResolutionCapability` into the injected
|
|
1425
|
+
* {@link NameResolver} seam consumed by `@openzeppelin/ui-components` (SF-3, D3).
|
|
1426
|
+
* Mounted ambiently by the renderer:
|
|
1427
|
+
*
|
|
1428
|
+
* ```tsx
|
|
1429
|
+
* <NameResolverProvider {...useRuntimeNameResolver()}>{form}</NameResolverProvider>
|
|
1430
|
+
* ```
|
|
1431
|
+
*
|
|
1432
|
+
* Each imperative call is backed by SF-2's **owned** resolution `QueryClient`
|
|
1433
|
+
* via `fetchQuery`, reusing SF-2's exact query-key convention
|
|
1434
|
+
* (`buildResolutionKey('name', networkId, normalizedName)`) — so a name
|
|
1435
|
+
* resolved through the field and the same name resolved by a bare
|
|
1436
|
+
* `useResolveName` hit the SAME cache entry: shared cache, dedupe, and
|
|
1437
|
+
* out-of-order safety, never a parallel cache (INV-119).
|
|
1438
|
+
*
|
|
1439
|
+
* Degradation, in order (all method-omission, never a throw):
|
|
1440
|
+
* - No `WalletStateProvider` mounted → empty resolver. The context is read
|
|
1441
|
+
* directly (not via `useWalletState()`, which throws) so the ambient
|
|
1442
|
+
* renderer mount stays safe for wallet-less usage.
|
|
1443
|
+
* - No active runtime, or runtime without the capability → empty resolver.
|
|
1444
|
+
* - Capability without `resolveName` → `isValidName` only; forward unsupported.
|
|
1445
|
+
*
|
|
1446
|
+
* When the capability is present but the network itself is unsupported, the
|
|
1447
|
+
* adapter's `resolveName` resolves `{ ok: false, error: UNSUPPORTED_NETWORK }`
|
|
1448
|
+
* and that result passes through unchanged.
|
|
1449
|
+
*
|
|
1450
|
+
* @returns A referentially stable {@link NameResolver} for the active runtime.
|
|
1451
|
+
*/
|
|
1452
|
+
function useRuntimeNameResolver() {
|
|
1453
|
+
const walletState = (0, react.useContext)(WalletStateContext);
|
|
1454
|
+
const { queryClient, config } = useNameResolutionContext();
|
|
1455
|
+
const activeRuntime = walletState?.activeRuntime ?? null;
|
|
1456
|
+
const capability = activeRuntime?.nameResolution;
|
|
1457
|
+
const networkId = walletState?.activeNetworkId ?? "";
|
|
1458
|
+
const runtimeInstanceId = getRuntimeInstanceId(activeRuntime);
|
|
1459
|
+
return (0, react.useMemo)(() => {
|
|
1460
|
+
if (!capability) return EMPTY_RESOLVER;
|
|
1461
|
+
const resolveNameMethod = capability.resolveName;
|
|
1462
|
+
return {
|
|
1463
|
+
isValidName: (name) => capability.isValidName(name),
|
|
1464
|
+
resolveName: resolveNameMethod ? async (name) => {
|
|
1465
|
+
const normalized = name.trim().toLowerCase();
|
|
1466
|
+
try {
|
|
1467
|
+
return {
|
|
1468
|
+
ok: true,
|
|
1469
|
+
value: await queryClient.fetchQuery({
|
|
1470
|
+
queryKey: buildResolutionKey("name", networkId, normalized, runtimeInstanceId),
|
|
1471
|
+
queryFn: async () => {
|
|
1472
|
+
let result;
|
|
1473
|
+
try {
|
|
1474
|
+
result = await resolveNameMethod(normalized);
|
|
1475
|
+
} catch (cause) {
|
|
1476
|
+
throw new ResolutionQueryError({
|
|
1477
|
+
code: "ADAPTER_ERROR",
|
|
1478
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
1479
|
+
cause
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
if (!result.ok) throw new ResolutionQueryError(result.error);
|
|
1483
|
+
return result.value;
|
|
1484
|
+
},
|
|
1485
|
+
staleTime: config.staleTimeMs,
|
|
1486
|
+
gcTime: config.gcTimeMs,
|
|
1487
|
+
retry: (failureCount, error) => (error instanceof ResolutionQueryError ? isTransientError(error.resolutionError) : true) && failureCount < config.transientRetryCount
|
|
1488
|
+
})
|
|
1489
|
+
};
|
|
1490
|
+
} catch (error) {
|
|
1491
|
+
return {
|
|
1492
|
+
ok: false,
|
|
1493
|
+
error: toNameResolutionError(error)
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
} : void 0
|
|
1497
|
+
};
|
|
1498
|
+
}, [
|
|
1499
|
+
capability,
|
|
1500
|
+
queryClient,
|
|
1501
|
+
config,
|
|
1502
|
+
networkId,
|
|
1503
|
+
runtimeInstanceId
|
|
1504
|
+
]);
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
//#endregion
|
|
1508
|
+
//#region src/hooks/runtime/runtimeCreationConfig.ts
|
|
1509
|
+
/** Safe default: miss-fallback OFF; no uiKit override. INV-204 */
|
|
1510
|
+
const DEFAULT_RUNTIME_CREATION_CONFIG = {};
|
|
1511
|
+
/**
|
|
1512
|
+
* Normalizes the opt-in flag to the adapter's strict-enable contract.
|
|
1513
|
+
* UIKit uses this at the threading boundary so `false` and `undefined` never
|
|
1514
|
+
* emit `enableMainnetL1MissFallback: false` into adapter options (absent = OFF).
|
|
1515
|
+
*/
|
|
1516
|
+
function isMainnetL1MissFallbackEnabled(options) {
|
|
1517
|
+
return options?.enableMainnetL1MissFallback === true;
|
|
1518
|
+
}
|
|
1519
|
+
/**
|
|
1520
|
+
* Builds the `createRuntime` options bag for adapter threading.
|
|
1521
|
+
* When OFF, `nameResolution` is omitted (INV-207). When ON, spreads only
|
|
1522
|
+
* `{ enableMainnetL1MissFallback: true }` (INV-208).
|
|
1523
|
+
*/
|
|
1524
|
+
function buildCreateRuntimeOptions(options) {
|
|
1525
|
+
return {
|
|
1526
|
+
...options?.uiKit !== void 0 ? { uiKit: options.uiKit } : {},
|
|
1527
|
+
...isMainnetL1MissFallbackEnabled(options?.nameResolution) ? { nameResolution: { enableMainnetL1MissFallback: true } } : {}
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
//#endregion
|
|
1532
|
+
//#region src/hooks/runtime/createResolveRuntime.ts
|
|
1533
|
+
/**
|
|
1534
|
+
* Build a `resolveRuntime` callback for {@link RuntimeProvider} that forwards
|
|
1535
|
+
* `CreateRuntimeOptions` to `ecosystemDefinition.createRuntime`.
|
|
1536
|
+
*
|
|
1537
|
+
* Does not cache — {@link RuntimeProvider} owns the per-network singleton registry.
|
|
1538
|
+
*/
|
|
1539
|
+
function createResolveRuntime(ecosystemDefinition, params) {
|
|
1540
|
+
const { profile, options } = params;
|
|
1541
|
+
const mergedOptions = buildCreateRuntimeOptions(options);
|
|
1542
|
+
return (networkConfig) => Promise.resolve(ecosystemDefinition.createRuntime(profile, networkConfig, mergedOptions));
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
//#endregion
|
|
1546
|
+
//#region src/hooks/runtime/useResolveRuntime.ts
|
|
1547
|
+
/**
|
|
1548
|
+
* Memoized {@link createResolveRuntime} for React apps. Recompute when
|
|
1549
|
+
* `ecosystemDefinition`, `profile`, or opt-in posture change (INV-217, INV-222).
|
|
1550
|
+
*
|
|
1551
|
+
* Changing the returned function identity MUST trigger runtime registry flush
|
|
1552
|
+
* in {@link RuntimeProvider} (INV-218).
|
|
1553
|
+
*/
|
|
1554
|
+
function useResolveRuntime(ecosystemDefinition, params) {
|
|
1555
|
+
const { profile, options } = params;
|
|
1556
|
+
const uiKit = options?.uiKit;
|
|
1557
|
+
const enableMainnetL1MissFallback = options?.nameResolution?.enableMainnetL1MissFallback;
|
|
1558
|
+
return (0, react.useMemo)(() => createResolveRuntime(ecosystemDefinition, {
|
|
1559
|
+
profile,
|
|
1560
|
+
options: {
|
|
1561
|
+
...uiKit !== void 0 ? { uiKit } : {},
|
|
1562
|
+
...enableMainnetL1MissFallback === true ? { nameResolution: { enableMainnetL1MissFallback: true } } : {}
|
|
1563
|
+
}
|
|
1564
|
+
}), [
|
|
1565
|
+
ecosystemDefinition,
|
|
1566
|
+
profile,
|
|
1567
|
+
uiKit,
|
|
1568
|
+
enableMainnetL1MissFallback
|
|
1569
|
+
]);
|
|
1570
|
+
}
|
|
1571
|
+
|
|
908
1572
|
//#endregion
|
|
909
1573
|
//#region src/components/WalletConnectionUI.tsx
|
|
910
1574
|
/**
|
|
@@ -1114,6 +1778,10 @@ const NetworkSwitchManager = ({ wallet, networkCatalog, targetNetworkId, onNetwo
|
|
|
1114
1778
|
//#endregion
|
|
1115
1779
|
exports.AnalyticsContext = AnalyticsContext;
|
|
1116
1780
|
exports.AnalyticsProvider = AnalyticsProvider;
|
|
1781
|
+
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
1782
|
+
exports.DEFAULT_RUNTIME_CREATION_CONFIG = DEFAULT_RUNTIME_CREATION_CONFIG;
|
|
1783
|
+
exports.NameResolutionContext = NameResolutionContext;
|
|
1784
|
+
exports.NameResolutionProvider = NameResolutionProvider;
|
|
1117
1785
|
exports.NetworkSwitchManager = NetworkSwitchManager;
|
|
1118
1786
|
exports.RuntimeContext = RuntimeContext;
|
|
1119
1787
|
exports.RuntimeProvider = RuntimeProvider;
|
|
@@ -1122,13 +1790,20 @@ exports.WalletConnectionHeader = WalletConnectionHeader;
|
|
|
1122
1790
|
exports.WalletConnectionUI = WalletConnectionUI;
|
|
1123
1791
|
exports.WalletStateContext = WalletStateContext;
|
|
1124
1792
|
exports.WalletStateProvider = WalletStateProvider;
|
|
1793
|
+
exports.createResolveRuntime = createResolveRuntime;
|
|
1794
|
+
exports.isMainnetL1MissFallbackEnabled = isMainnetL1MissFallbackEnabled;
|
|
1125
1795
|
exports.useAnalytics = useAnalytics;
|
|
1126
1796
|
exports.useDerivedAccountStatus = useDerivedAccountStatus;
|
|
1127
1797
|
exports.useDerivedChainInfo = useDerivedChainInfo;
|
|
1128
1798
|
exports.useDerivedConnectStatus = useDerivedConnectStatus;
|
|
1129
1799
|
exports.useDerivedDisconnect = useDerivedDisconnect;
|
|
1130
1800
|
exports.useDerivedSwitchChainStatus = useDerivedSwitchChainStatus;
|
|
1801
|
+
exports.useNameResolutionContext = useNameResolutionContext;
|
|
1802
|
+
exports.useResolveAddress = useResolveAddress;
|
|
1803
|
+
exports.useResolveName = useResolveName;
|
|
1804
|
+
exports.useResolveRuntime = useResolveRuntime;
|
|
1131
1805
|
exports.useRuntimeContext = useRuntimeContext;
|
|
1806
|
+
exports.useRuntimeNameResolver = useRuntimeNameResolver;
|
|
1132
1807
|
exports.useWalletComponents = useWalletComponents;
|
|
1133
1808
|
exports.useWalletReconnectionHandler = useWalletReconnectionHandler;
|
|
1134
1809
|
exports.useWalletState = useWalletState;
|