@volr/react 0.1.20 → 0.1.22
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/index.cjs +567 -320
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +132 -6
- package/dist/index.d.ts +132 -6
- package/dist/index.js +560 -321
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9306,6 +9306,21 @@ var APIClient = class {
|
|
|
9306
9306
|
}
|
|
9307
9307
|
};
|
|
9308
9308
|
|
|
9309
|
+
// src/config/backend.ts
|
|
9310
|
+
var DEFAULT_API_BASE_URL = "https://api.volr.io";
|
|
9311
|
+
function resolveApiBaseUrl(config) {
|
|
9312
|
+
const anyConfig = config;
|
|
9313
|
+
const devOverride = anyConfig.__devApiBaseUrl;
|
|
9314
|
+
if (devOverride && typeof devOverride === "string") {
|
|
9315
|
+
return devOverride.replace(/\/+$/, "");
|
|
9316
|
+
}
|
|
9317
|
+
const override = anyConfig.apiBaseUrl;
|
|
9318
|
+
if (override && typeof override === "string") {
|
|
9319
|
+
return override.replace(/\/+$/, "");
|
|
9320
|
+
}
|
|
9321
|
+
return DEFAULT_API_BASE_URL;
|
|
9322
|
+
}
|
|
9323
|
+
|
|
9309
9324
|
// src/headless/auth-sync.ts
|
|
9310
9325
|
var SessionSync = class {
|
|
9311
9326
|
constructor() {
|
|
@@ -9319,11 +9334,12 @@ var SessionSync = class {
|
|
|
9319
9334
|
}
|
|
9320
9335
|
if (typeof window !== "undefined") {
|
|
9321
9336
|
window.addEventListener("storage", (e) => {
|
|
9322
|
-
if (e.key === STORAGE_KEYS.accessToken || e.key === STORAGE_KEYS.user) {
|
|
9337
|
+
if (e.key === STORAGE_KEYS.accessToken || e.key === STORAGE_KEYS.refreshToken || e.key === STORAGE_KEYS.user) {
|
|
9323
9338
|
this.notifyListeners({
|
|
9324
9339
|
type: "REFRESH",
|
|
9325
9340
|
payload: {
|
|
9326
|
-
accessToken: safeStorage.getItem(STORAGE_KEYS.accessToken) || ""
|
|
9341
|
+
accessToken: safeStorage.getItem(STORAGE_KEYS.accessToken) || "",
|
|
9342
|
+
refreshToken: safeStorage.getItem(STORAGE_KEYS.refreshToken)
|
|
9327
9343
|
}
|
|
9328
9344
|
});
|
|
9329
9345
|
}
|
|
@@ -9377,19 +9393,63 @@ var SessionSync = class {
|
|
|
9377
9393
|
}
|
|
9378
9394
|
};
|
|
9379
9395
|
|
|
9380
|
-
// src/
|
|
9381
|
-
|
|
9382
|
-
|
|
9383
|
-
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
|
|
9396
|
+
// src/react/hooks/useSessionSync.ts
|
|
9397
|
+
function useSessionSync({
|
|
9398
|
+
client,
|
|
9399
|
+
config,
|
|
9400
|
+
setAccessTokenState,
|
|
9401
|
+
setRefreshTokenState,
|
|
9402
|
+
setUser,
|
|
9403
|
+
setProviderState
|
|
9404
|
+
}) {
|
|
9405
|
+
const syncRef = useRef(null);
|
|
9406
|
+
useEffect(() => {
|
|
9407
|
+
syncRef.current = new SessionSync();
|
|
9408
|
+
const unsubscribe = syncRef.current.subscribe((event) => {
|
|
9409
|
+
if (event.type === "LOGIN") {
|
|
9410
|
+
client.setAccessToken(event.payload.accessToken);
|
|
9411
|
+
setAccessTokenState(event.payload.accessToken);
|
|
9412
|
+
client.setRefreshToken(event.payload.refreshToken);
|
|
9413
|
+
setRefreshTokenState(event.payload.refreshToken);
|
|
9414
|
+
setUser(event.payload.user);
|
|
9415
|
+
safeStorage.setItem(
|
|
9416
|
+
STORAGE_KEYS.user,
|
|
9417
|
+
JSON.stringify(event.payload.user)
|
|
9418
|
+
);
|
|
9419
|
+
client.setApiKey(config.projectApiKey);
|
|
9420
|
+
} else if (event.type === "LOGOUT") {
|
|
9421
|
+
client.setAccessToken(null);
|
|
9422
|
+
client.setRefreshToken(null);
|
|
9423
|
+
setAccessTokenState(null);
|
|
9424
|
+
setRefreshTokenState(null);
|
|
9425
|
+
setUser(null);
|
|
9426
|
+
setProviderState(null);
|
|
9427
|
+
safeStorage.removeItem(STORAGE_KEYS.user);
|
|
9428
|
+
safeStorage.removeItem(STORAGE_KEYS.provider);
|
|
9429
|
+
} else if (event.type === "REFRESH") {
|
|
9430
|
+
client.setAccessToken(event.payload.accessToken);
|
|
9431
|
+
setAccessTokenState(event.payload.accessToken);
|
|
9432
|
+
if (event.payload.refreshToken) {
|
|
9433
|
+
client.setRefreshToken(event.payload.refreshToken);
|
|
9434
|
+
setRefreshTokenState(event.payload.refreshToken);
|
|
9435
|
+
}
|
|
9436
|
+
} else if (event.type === "PROVIDER_SET") {
|
|
9437
|
+
setUser(
|
|
9438
|
+
(prev) => prev ? {
|
|
9439
|
+
...prev,
|
|
9440
|
+
keyStorageType: event.payload.keyStorageType,
|
|
9441
|
+
address: event.payload.address
|
|
9442
|
+
} : null
|
|
9443
|
+
);
|
|
9444
|
+
safeStorage.setItem(STORAGE_KEYS.provider, event.payload.keyStorageType);
|
|
9445
|
+
}
|
|
9446
|
+
});
|
|
9447
|
+
return () => {
|
|
9448
|
+
unsubscribe();
|
|
9449
|
+
syncRef.current?.destroy();
|
|
9450
|
+
};
|
|
9451
|
+
}, [client, config.projectApiKey, setAccessTokenState, setRefreshTokenState, setUser, setProviderState]);
|
|
9452
|
+
return syncRef;
|
|
9393
9453
|
}
|
|
9394
9454
|
|
|
9395
9455
|
// src/config/webauthn.ts
|
|
@@ -9652,6 +9712,120 @@ async function restorePasskey(params) {
|
|
|
9652
9712
|
provider
|
|
9653
9713
|
};
|
|
9654
9714
|
}
|
|
9715
|
+
|
|
9716
|
+
// src/react/hooks/useAutoRecover.ts
|
|
9717
|
+
function useAutoRecover({
|
|
9718
|
+
client,
|
|
9719
|
+
config,
|
|
9720
|
+
setAccessTokenState,
|
|
9721
|
+
setRefreshTokenState,
|
|
9722
|
+
setUser,
|
|
9723
|
+
setProviderState,
|
|
9724
|
+
setProvider,
|
|
9725
|
+
setIsLoading
|
|
9726
|
+
}) {
|
|
9727
|
+
const REQUIRE_USER_GESTURE_TO_RESTORE = true;
|
|
9728
|
+
const hasRecoveredRef = useRef(false);
|
|
9729
|
+
const restorationAttemptedRef = useRef(false);
|
|
9730
|
+
useEffect(() => {
|
|
9731
|
+
if (config.autoRecoverOnLogin !== false && !hasRecoveredRef.current) {
|
|
9732
|
+
hasRecoveredRef.current = true;
|
|
9733
|
+
const recover = async () => {
|
|
9734
|
+
try {
|
|
9735
|
+
setIsLoading(true);
|
|
9736
|
+
if (!client.getRefreshToken()) {
|
|
9737
|
+
console.log("[Provider] No refresh token found, skipping auto-recover");
|
|
9738
|
+
setIsLoading(false);
|
|
9739
|
+
return;
|
|
9740
|
+
}
|
|
9741
|
+
const refreshedUser = await client.refreshSession();
|
|
9742
|
+
console.log("[Provider] Session refreshed, user:", refreshedUser);
|
|
9743
|
+
setAccessTokenState(client.getAccessToken());
|
|
9744
|
+
setRefreshTokenState(client.getRefreshToken());
|
|
9745
|
+
if (refreshedUser) {
|
|
9746
|
+
setUser(refreshedUser);
|
|
9747
|
+
if (!REQUIRE_USER_GESTURE_TO_RESTORE) ; else if (refreshedUser.keyStorageType === "passkey") {
|
|
9748
|
+
console.log("[Provider] TTL=0 mode: Provider restoration deferred");
|
|
9749
|
+
if (!refreshedUser.blobUrl || !refreshedUser.prfInput) {
|
|
9750
|
+
console.warn("[Provider] Passkey user missing blobUrl or prfInput");
|
|
9751
|
+
}
|
|
9752
|
+
}
|
|
9753
|
+
} else {
|
|
9754
|
+
const userStr = safeStorage.getItem(STORAGE_KEYS.user);
|
|
9755
|
+
if (userStr) {
|
|
9756
|
+
try {
|
|
9757
|
+
setUser(JSON.parse(userStr));
|
|
9758
|
+
} catch {
|
|
9759
|
+
safeStorage.removeItem(STORAGE_KEYS.user);
|
|
9760
|
+
}
|
|
9761
|
+
}
|
|
9762
|
+
}
|
|
9763
|
+
} catch {
|
|
9764
|
+
client.setAccessToken(null);
|
|
9765
|
+
client.setRefreshToken(null);
|
|
9766
|
+
setAccessTokenState(null);
|
|
9767
|
+
setRefreshTokenState(null);
|
|
9768
|
+
setUser(null);
|
|
9769
|
+
setProviderState(null);
|
|
9770
|
+
safeStorage.removeItem(STORAGE_KEYS.user);
|
|
9771
|
+
safeStorage.removeItem(STORAGE_KEYS.provider);
|
|
9772
|
+
} finally {
|
|
9773
|
+
setIsLoading(false);
|
|
9774
|
+
}
|
|
9775
|
+
};
|
|
9776
|
+
recover();
|
|
9777
|
+
} else {
|
|
9778
|
+
setIsLoading(false);
|
|
9779
|
+
}
|
|
9780
|
+
}, [client, config.autoRecoverOnLogin, setAccessTokenState, setRefreshTokenState, setUser, setProviderState, setProvider, setIsLoading]);
|
|
9781
|
+
}
|
|
9782
|
+
function useWalletEvents({
|
|
9783
|
+
user,
|
|
9784
|
+
logout,
|
|
9785
|
+
setUser,
|
|
9786
|
+
setProviderState
|
|
9787
|
+
}) {
|
|
9788
|
+
useEffect(() => {
|
|
9789
|
+
if (!user) return;
|
|
9790
|
+
if (typeof window === "undefined" || !window.ethereum) {
|
|
9791
|
+
return;
|
|
9792
|
+
}
|
|
9793
|
+
const ethereum = window.ethereum;
|
|
9794
|
+
const handleAccountsChanged = (accounts) => {
|
|
9795
|
+
console.log("[Provider] accountsChanged event:", accounts);
|
|
9796
|
+
if (accounts.length === 0) {
|
|
9797
|
+
console.warn("[Provider] Wallet disconnected, logging out...");
|
|
9798
|
+
setTimeout(() => logout(), 3e3);
|
|
9799
|
+
} else if (user.evmAddress && accounts[0].toLowerCase() !== user.evmAddress.toLowerCase()) {
|
|
9800
|
+
console.warn("[Provider] Account changed, logging out in 3 seconds...");
|
|
9801
|
+
setTimeout(() => logout(), 3e3);
|
|
9802
|
+
}
|
|
9803
|
+
};
|
|
9804
|
+
const handleChainChanged = (chainIdHex) => {
|
|
9805
|
+
const newChainId = parseInt(chainIdHex, 16);
|
|
9806
|
+
console.log("[Provider] chainChanged event:", newChainId);
|
|
9807
|
+
setUser(
|
|
9808
|
+
(prev) => prev ? { ...prev, lastWalletChainId: newChainId } : null
|
|
9809
|
+
);
|
|
9810
|
+
window.location.reload();
|
|
9811
|
+
};
|
|
9812
|
+
const handleDisconnect = () => {
|
|
9813
|
+
console.log("[Provider] disconnect event");
|
|
9814
|
+
setProviderState(null);
|
|
9815
|
+
safeStorage.removeItem(STORAGE_KEYS.provider);
|
|
9816
|
+
};
|
|
9817
|
+
ethereum.on("accountsChanged", handleAccountsChanged);
|
|
9818
|
+
ethereum.on("chainChanged", handleChainChanged);
|
|
9819
|
+
ethereum.on("disconnect", handleDisconnect);
|
|
9820
|
+
return () => {
|
|
9821
|
+
ethereum.removeListener("accountsChanged", handleAccountsChanged);
|
|
9822
|
+
ethereum.removeListener("chainChanged", handleChainChanged);
|
|
9823
|
+
ethereum.removeListener("disconnect", handleDisconnect);
|
|
9824
|
+
};
|
|
9825
|
+
}, [user, logout, setUser, setProviderState]);
|
|
9826
|
+
}
|
|
9827
|
+
|
|
9828
|
+
// src/utils/json.ts
|
|
9655
9829
|
function serializeBigIntDeep(obj) {
|
|
9656
9830
|
if (obj === null || obj === void 0) {
|
|
9657
9831
|
return obj;
|
|
@@ -9673,8 +9847,62 @@ function serializeBigIntDeep(obj) {
|
|
|
9673
9847
|
}
|
|
9674
9848
|
return obj;
|
|
9675
9849
|
}
|
|
9850
|
+
|
|
9851
|
+
// src/react/hooks/useVolrActions.ts
|
|
9852
|
+
function useVolrActions({
|
|
9853
|
+
client,
|
|
9854
|
+
setError
|
|
9855
|
+
}) {
|
|
9856
|
+
const precheck = useCallback(
|
|
9857
|
+
async (input) => {
|
|
9858
|
+
try {
|
|
9859
|
+
setError(null);
|
|
9860
|
+
await client.ensureAccessToken();
|
|
9861
|
+
const response = await client.post(
|
|
9862
|
+
"/wallet/precheck",
|
|
9863
|
+
input
|
|
9864
|
+
);
|
|
9865
|
+
return response.quote;
|
|
9866
|
+
} catch (err) {
|
|
9867
|
+
const error = err instanceof Error ? err : new Error("Precheck failed");
|
|
9868
|
+
const diag = err?.response?.data?.error?.developerDiagnostics;
|
|
9869
|
+
if (diag) {
|
|
9870
|
+
console.error("[volr][precheck] developerDiagnostics:", diag);
|
|
9871
|
+
}
|
|
9872
|
+
setError(error);
|
|
9873
|
+
throw error;
|
|
9874
|
+
}
|
|
9875
|
+
},
|
|
9876
|
+
[client, setError]
|
|
9877
|
+
);
|
|
9878
|
+
const relay = useCallback(
|
|
9879
|
+
async (input, opts) => {
|
|
9880
|
+
try {
|
|
9881
|
+
setError(null);
|
|
9882
|
+
await client.ensureAccessToken();
|
|
9883
|
+
const idempotencyKey = opts?.idempotencyKey ?? (typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`);
|
|
9884
|
+
const serializedInput = serializeBigIntDeep(input);
|
|
9885
|
+
const response = await client.post(
|
|
9886
|
+
"/wallet/relay",
|
|
9887
|
+
serializedInput,
|
|
9888
|
+
idempotencyKey
|
|
9889
|
+
);
|
|
9890
|
+
return response;
|
|
9891
|
+
} catch (err) {
|
|
9892
|
+
const error = err instanceof Error ? err : new Error("Relay failed");
|
|
9893
|
+
const diag = err?.response?.data?.error?.developerDiagnostics;
|
|
9894
|
+
if (diag) {
|
|
9895
|
+
console.error("[volr][relay] developerDiagnostics:", diag);
|
|
9896
|
+
}
|
|
9897
|
+
setError(error);
|
|
9898
|
+
throw error;
|
|
9899
|
+
}
|
|
9900
|
+
},
|
|
9901
|
+
[client, setError]
|
|
9902
|
+
);
|
|
9903
|
+
return { precheck, relay };
|
|
9904
|
+
}
|
|
9676
9905
|
function VolrProvider({ config, children }) {
|
|
9677
|
-
const REQUIRE_USER_GESTURE_TO_RESTORE = true;
|
|
9678
9906
|
const providerCountRef = useRef(0);
|
|
9679
9907
|
useEffect(() => {
|
|
9680
9908
|
providerCountRef.current++;
|
|
@@ -9723,45 +9951,14 @@ function VolrProvider({ config, children }) {
|
|
|
9723
9951
|
const [refreshToken, setRefreshTokenState] = useState(() => {
|
|
9724
9952
|
return client.getRefreshToken();
|
|
9725
9953
|
});
|
|
9726
|
-
const syncRef =
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
|
|
9734
|
-
safeStorage.setItem(
|
|
9735
|
-
STORAGE_KEYS.user,
|
|
9736
|
-
JSON.stringify(event.payload.user)
|
|
9737
|
-
);
|
|
9738
|
-
client.setApiKey(config.projectApiKey);
|
|
9739
|
-
} else if (event.type === "LOGOUT") {
|
|
9740
|
-
client.setAccessToken(null);
|
|
9741
|
-
client.setRefreshToken(null);
|
|
9742
|
-
setAccessTokenState(null);
|
|
9743
|
-
setRefreshTokenState(null);
|
|
9744
|
-
setUser(null);
|
|
9745
|
-
setProviderState(null);
|
|
9746
|
-
safeStorage.removeItem(STORAGE_KEYS.user);
|
|
9747
|
-
safeStorage.removeItem(STORAGE_KEYS.provider);
|
|
9748
|
-
} else if (event.type === "REFRESH") {
|
|
9749
|
-
client.setAccessToken(event.payload.accessToken);
|
|
9750
|
-
setAccessTokenState(event.payload.accessToken);
|
|
9751
|
-
} else if (event.type === "PROVIDER_SET") {
|
|
9752
|
-
setUser((prev) => ({
|
|
9753
|
-
...prev,
|
|
9754
|
-
keyStorageType: event.payload.keyStorageType,
|
|
9755
|
-
address: event.payload.address
|
|
9756
|
-
}));
|
|
9757
|
-
safeStorage.setItem(STORAGE_KEYS.provider, event.payload.keyStorageType);
|
|
9758
|
-
}
|
|
9759
|
-
});
|
|
9760
|
-
return () => {
|
|
9761
|
-
unsubscribe();
|
|
9762
|
-
syncRef.current?.destroy();
|
|
9763
|
-
};
|
|
9764
|
-
}, [client, config.projectApiKey]);
|
|
9954
|
+
const syncRef = useSessionSync({
|
|
9955
|
+
client,
|
|
9956
|
+
config,
|
|
9957
|
+
setAccessTokenState,
|
|
9958
|
+
setRefreshTokenState,
|
|
9959
|
+
setUser,
|
|
9960
|
+
setProviderState
|
|
9961
|
+
});
|
|
9765
9962
|
const setProvider = useCallback(
|
|
9766
9963
|
async (newProvider) => {
|
|
9767
9964
|
try {
|
|
@@ -9793,108 +9990,22 @@ function VolrProvider({ config, children }) {
|
|
|
9793
9990
|
throw error2;
|
|
9794
9991
|
}
|
|
9795
9992
|
},
|
|
9796
|
-
[client, user]
|
|
9797
|
-
);
|
|
9798
|
-
const hasRecoveredRef = useRef(false);
|
|
9799
|
-
const restorationAttemptedRef = useRef(false);
|
|
9800
|
-
useEffect(() => {
|
|
9801
|
-
if (config.autoRecoverOnLogin !== false && !hasRecoveredRef.current) {
|
|
9802
|
-
hasRecoveredRef.current = true;
|
|
9803
|
-
const recover = async () => {
|
|
9804
|
-
try {
|
|
9805
|
-
setIsLoading(true);
|
|
9806
|
-
if (!client.getRefreshToken()) {
|
|
9807
|
-
console.log("[Provider] No refresh token found, skipping auto-recover");
|
|
9808
|
-
setIsLoading(false);
|
|
9809
|
-
return;
|
|
9810
|
-
}
|
|
9811
|
-
const refreshedUser = await client.refreshSession();
|
|
9812
|
-
console.log("[Provider] Session refreshed, user:", refreshedUser);
|
|
9813
|
-
setAccessTokenState(client.getAccessToken());
|
|
9814
|
-
setRefreshTokenState(client.getRefreshToken());
|
|
9815
|
-
if (refreshedUser) {
|
|
9816
|
-
setUser(refreshedUser);
|
|
9817
|
-
if (!REQUIRE_USER_GESTURE_TO_RESTORE) ; else if (refreshedUser.keyStorageType === "passkey") {
|
|
9818
|
-
console.log("[Provider] TTL=0 mode: Provider restoration deferred");
|
|
9819
|
-
if (!refreshedUser.blobUrl || !refreshedUser.prfInput) {
|
|
9820
|
-
console.warn("[Provider] Passkey user missing blobUrl or prfInput");
|
|
9821
|
-
}
|
|
9822
|
-
}
|
|
9823
|
-
} else {
|
|
9824
|
-
const userStr = safeStorage.getItem(STORAGE_KEYS.user);
|
|
9825
|
-
if (userStr) {
|
|
9826
|
-
try {
|
|
9827
|
-
setUser(JSON.parse(userStr));
|
|
9828
|
-
} catch {
|
|
9829
|
-
safeStorage.removeItem(STORAGE_KEYS.user);
|
|
9830
|
-
}
|
|
9831
|
-
}
|
|
9832
|
-
}
|
|
9833
|
-
} catch {
|
|
9834
|
-
client.setAccessToken(null);
|
|
9835
|
-
client.setRefreshToken(null);
|
|
9836
|
-
setAccessTokenState(null);
|
|
9837
|
-
setRefreshTokenState(null);
|
|
9838
|
-
setUser(null);
|
|
9839
|
-
setProviderState(null);
|
|
9840
|
-
safeStorage.removeItem(STORAGE_KEYS.user);
|
|
9841
|
-
safeStorage.removeItem(STORAGE_KEYS.provider);
|
|
9842
|
-
} finally {
|
|
9843
|
-
setIsLoading(false);
|
|
9844
|
-
}
|
|
9845
|
-
};
|
|
9846
|
-
recover();
|
|
9847
|
-
} else {
|
|
9848
|
-
setIsLoading(false);
|
|
9849
|
-
}
|
|
9850
|
-
}, [client, config.autoRecoverOnLogin]);
|
|
9851
|
-
const precheck = useCallback(
|
|
9852
|
-
async (input) => {
|
|
9853
|
-
try {
|
|
9854
|
-
setError(null);
|
|
9855
|
-
await client.ensureAccessToken();
|
|
9856
|
-
const response = await client.post(
|
|
9857
|
-
"/wallet/precheck",
|
|
9858
|
-
input
|
|
9859
|
-
);
|
|
9860
|
-
return response.quote;
|
|
9861
|
-
} catch (err) {
|
|
9862
|
-
const error2 = err instanceof Error ? err : new Error("Precheck failed");
|
|
9863
|
-
const diag = err?.response?.data?.error?.developerDiagnostics;
|
|
9864
|
-
if (diag) {
|
|
9865
|
-
console.error("[volr][precheck] developerDiagnostics:", diag);
|
|
9866
|
-
}
|
|
9867
|
-
setError(error2);
|
|
9868
|
-
throw error2;
|
|
9869
|
-
}
|
|
9870
|
-
},
|
|
9871
|
-
[client]
|
|
9872
|
-
);
|
|
9873
|
-
const relay = useCallback(
|
|
9874
|
-
async (input, opts) => {
|
|
9875
|
-
try {
|
|
9876
|
-
setError(null);
|
|
9877
|
-
await client.ensureAccessToken();
|
|
9878
|
-
const idempotencyKey = opts?.idempotencyKey ?? (typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`);
|
|
9879
|
-
const serializedInput = serializeBigIntDeep(input);
|
|
9880
|
-
const response = await client.post(
|
|
9881
|
-
"/wallet/relay",
|
|
9882
|
-
serializedInput,
|
|
9883
|
-
idempotencyKey
|
|
9884
|
-
);
|
|
9885
|
-
return response;
|
|
9886
|
-
} catch (err) {
|
|
9887
|
-
const error2 = err instanceof Error ? err : new Error("Relay failed");
|
|
9888
|
-
const diag = err?.response?.data?.error?.developerDiagnostics;
|
|
9889
|
-
if (diag) {
|
|
9890
|
-
console.error("[volr][relay] developerDiagnostics:", diag);
|
|
9891
|
-
}
|
|
9892
|
-
setError(error2);
|
|
9893
|
-
throw error2;
|
|
9894
|
-
}
|
|
9895
|
-
},
|
|
9896
|
-
[client]
|
|
9993
|
+
[client, user, syncRef]
|
|
9897
9994
|
);
|
|
9995
|
+
useAutoRecover({
|
|
9996
|
+
client,
|
|
9997
|
+
config,
|
|
9998
|
+
setAccessTokenState,
|
|
9999
|
+
setRefreshTokenState,
|
|
10000
|
+
setUser,
|
|
10001
|
+
setProviderState,
|
|
10002
|
+
setProvider,
|
|
10003
|
+
setIsLoading
|
|
10004
|
+
});
|
|
10005
|
+
const { precheck, relay } = useVolrActions({
|
|
10006
|
+
client,
|
|
10007
|
+
setError
|
|
10008
|
+
});
|
|
9898
10009
|
const logout = useCallback(async () => {
|
|
9899
10010
|
try {
|
|
9900
10011
|
setError(null);
|
|
@@ -9914,45 +10025,13 @@ function VolrProvider({ config, children }) {
|
|
|
9914
10025
|
setError(error2);
|
|
9915
10026
|
throw error2;
|
|
9916
10027
|
}
|
|
9917
|
-
}, [client]);
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
const handleAccountsChanged = (accounts) => {
|
|
9925
|
-
console.log("[Provider] accountsChanged event:", accounts);
|
|
9926
|
-
if (accounts.length === 0) {
|
|
9927
|
-
console.warn("[Provider] Wallet disconnected, logging out...");
|
|
9928
|
-
setTimeout(() => logout(), 3e3);
|
|
9929
|
-
} else if (user.evmAddress && accounts[0].toLowerCase() !== user.evmAddress.toLowerCase()) {
|
|
9930
|
-
console.warn("[Provider] Account changed, logging out in 3 seconds...");
|
|
9931
|
-
setTimeout(() => logout(), 3e3);
|
|
9932
|
-
}
|
|
9933
|
-
};
|
|
9934
|
-
const handleChainChanged = (chainIdHex) => {
|
|
9935
|
-
const newChainId = parseInt(chainIdHex, 16);
|
|
9936
|
-
console.log("[Provider] chainChanged event:", newChainId);
|
|
9937
|
-
setUser(
|
|
9938
|
-
(prev) => prev ? { ...prev, lastWalletChainId: newChainId } : null
|
|
9939
|
-
);
|
|
9940
|
-
window.location.reload();
|
|
9941
|
-
};
|
|
9942
|
-
const handleDisconnect = () => {
|
|
9943
|
-
console.log("[Provider] disconnect event");
|
|
9944
|
-
setProviderState(null);
|
|
9945
|
-
safeStorage.removeItem(STORAGE_KEYS.provider);
|
|
9946
|
-
};
|
|
9947
|
-
ethereum.on("accountsChanged", handleAccountsChanged);
|
|
9948
|
-
ethereum.on("chainChanged", handleChainChanged);
|
|
9949
|
-
ethereum.on("disconnect", handleDisconnect);
|
|
9950
|
-
return () => {
|
|
9951
|
-
ethereum.removeListener("accountsChanged", handleAccountsChanged);
|
|
9952
|
-
ethereum.removeListener("chainChanged", handleChainChanged);
|
|
9953
|
-
ethereum.removeListener("disconnect", handleDisconnect);
|
|
9954
|
-
};
|
|
9955
|
-
}, [user, logout, setUser]);
|
|
10028
|
+
}, [client, syncRef]);
|
|
10029
|
+
useWalletEvents({
|
|
10030
|
+
user,
|
|
10031
|
+
logout,
|
|
10032
|
+
setUser,
|
|
10033
|
+
setProviderState
|
|
10034
|
+
});
|
|
9956
10035
|
const publicValue = useMemo(
|
|
9957
10036
|
() => ({
|
|
9958
10037
|
config,
|
|
@@ -17842,6 +17921,8 @@ function http(url, config = {}) {
|
|
|
17842
17921
|
// ../node_modules/viem/_esm/index.js
|
|
17843
17922
|
init_encodeFunctionData();
|
|
17844
17923
|
init_getAddress();
|
|
17924
|
+
init_formatEther();
|
|
17925
|
+
init_formatUnits();
|
|
17845
17926
|
|
|
17846
17927
|
// src/utils/normalize.ts
|
|
17847
17928
|
function normalizeHex(value) {
|
|
@@ -17999,112 +18080,6 @@ function normalizeCall(call2) {
|
|
|
17999
18080
|
function normalizeCalls(calls) {
|
|
18000
18081
|
return calls.map(normalizeCall);
|
|
18001
18082
|
}
|
|
18002
|
-
|
|
18003
|
-
// src/wallet/preflight.ts
|
|
18004
|
-
function extractErrorMessage(err) {
|
|
18005
|
-
if (err && typeof err === "object") {
|
|
18006
|
-
const e = err;
|
|
18007
|
-
const parts = [];
|
|
18008
|
-
if (typeof e.shortMessage === "string") parts.push(e.shortMessage);
|
|
18009
|
-
if (typeof e.message === "string") parts.push(e.message);
|
|
18010
|
-
if (typeof e.cause?.message === "string") parts.push(e.cause.message);
|
|
18011
|
-
if (typeof e.details === "string") parts.push(e.details);
|
|
18012
|
-
const msg = parts.filter(Boolean).join(" | ");
|
|
18013
|
-
if (msg) return msg;
|
|
18014
|
-
}
|
|
18015
|
-
try {
|
|
18016
|
-
return String(err);
|
|
18017
|
-
} catch {
|
|
18018
|
-
return "Unknown error";
|
|
18019
|
-
}
|
|
18020
|
-
}
|
|
18021
|
-
function isIgnorableFundingError(err, message) {
|
|
18022
|
-
const m = message.toLowerCase();
|
|
18023
|
-
const e = err;
|
|
18024
|
-
const name = typeof e?.name === "string" ? e.name.toLowerCase() : "";
|
|
18025
|
-
if (name === "insufficientfundserror" || name.includes("insufficientfunds")) {
|
|
18026
|
-
return true;
|
|
18027
|
-
}
|
|
18028
|
-
if (typeof e?.code === "string") {
|
|
18029
|
-
const code = e.code.toLowerCase();
|
|
18030
|
-
if (code.includes("execution_reverted") || code.includes("call_exception")) {
|
|
18031
|
-
return false;
|
|
18032
|
-
}
|
|
18033
|
-
if (code.includes("insufficient_funds")) {
|
|
18034
|
-
return true;
|
|
18035
|
-
}
|
|
18036
|
-
}
|
|
18037
|
-
const fundingPatterns = [
|
|
18038
|
-
"insufficient funds for gas * price",
|
|
18039
|
-
"insufficient funds for gas",
|
|
18040
|
-
"for gas * price + value",
|
|
18041
|
-
"not enough funds for l1 fee",
|
|
18042
|
-
"insufficient funds for l1 fee",
|
|
18043
|
-
"insufficient balance for gas",
|
|
18044
|
-
"account balance too low"
|
|
18045
|
-
];
|
|
18046
|
-
const hasFundingPattern = fundingPatterns.some((pattern) => m.includes(pattern));
|
|
18047
|
-
const hasRevertIndicator = m.includes("revert") || m.includes("execution reverted") || m.includes("call exception") || m.includes("vm execution error");
|
|
18048
|
-
return hasFundingPattern && !hasRevertIndicator;
|
|
18049
|
-
}
|
|
18050
|
-
async function preflightEstimate(publicClient, from14, calls, opts) {
|
|
18051
|
-
for (let i = 0; i < calls.length; i++) {
|
|
18052
|
-
const c = calls[i];
|
|
18053
|
-
try {
|
|
18054
|
-
await publicClient.estimateGas({
|
|
18055
|
-
account: from14,
|
|
18056
|
-
to: c.target,
|
|
18057
|
-
data: c.data,
|
|
18058
|
-
value: c.value ?? 0n
|
|
18059
|
-
});
|
|
18060
|
-
} catch (e) {
|
|
18061
|
-
const message = extractErrorMessage(e);
|
|
18062
|
-
const tolerateFunding = opts?.tolerateFundingErrors !== false;
|
|
18063
|
-
const isFundingError = isIgnorableFundingError(e, message);
|
|
18064
|
-
if (tolerateFunding && isFundingError) {
|
|
18065
|
-
try {
|
|
18066
|
-
await publicClient.call({
|
|
18067
|
-
account: from14,
|
|
18068
|
-
to: c.target,
|
|
18069
|
-
data: c.data,
|
|
18070
|
-
value: c.value ?? 0n
|
|
18071
|
-
});
|
|
18072
|
-
console.log(
|
|
18073
|
-
`[preflightEstimate] Ignoring funding error for call #${i} (target ${c.target}): ${message}`
|
|
18074
|
-
);
|
|
18075
|
-
continue;
|
|
18076
|
-
} catch (callError) {
|
|
18077
|
-
const callMessage = extractErrorMessage(callError);
|
|
18078
|
-
console.error(
|
|
18079
|
-
`[preflightEstimate] Static call after funding error also failed for call #${i} (target ${c.target}):`,
|
|
18080
|
-
{
|
|
18081
|
-
originalError: { message, errorName: e?.name, errorCode: e?.code },
|
|
18082
|
-
callError: {
|
|
18083
|
-
message: callMessage,
|
|
18084
|
-
name: callError?.name,
|
|
18085
|
-
code: callError?.code
|
|
18086
|
-
}
|
|
18087
|
-
}
|
|
18088
|
-
);
|
|
18089
|
-
throw new Error(
|
|
18090
|
-
`Preflight failed (call #${i} target ${c.target}): ${callMessage}`
|
|
18091
|
-
);
|
|
18092
|
-
}
|
|
18093
|
-
}
|
|
18094
|
-
console.error(
|
|
18095
|
-
`[preflightEstimate] Preflight failed for call #${i} (target ${c.target}):`,
|
|
18096
|
-
{
|
|
18097
|
-
message,
|
|
18098
|
-
errorName: e?.name,
|
|
18099
|
-
errorCode: e?.code,
|
|
18100
|
-
isFundingError,
|
|
18101
|
-
tolerateFunding
|
|
18102
|
-
}
|
|
18103
|
-
);
|
|
18104
|
-
throw new Error(`Preflight failed (call #${i} target ${c.target}): ${message}`);
|
|
18105
|
-
}
|
|
18106
|
-
}
|
|
18107
|
-
}
|
|
18108
18083
|
async function resolveSigner(input) {
|
|
18109
18084
|
const { explicitSigner, provider, chainId, client, user, setProvider } = input;
|
|
18110
18085
|
if (explicitSigner) {
|
|
@@ -18201,6 +18176,40 @@ function finalizeAuthWithNonce(tempAuth, quote) {
|
|
|
18201
18176
|
}
|
|
18202
18177
|
|
|
18203
18178
|
// src/utils/tx-polling.ts
|
|
18179
|
+
function extractFailureReason(response) {
|
|
18180
|
+
const reasons = [];
|
|
18181
|
+
const meta = response.meta;
|
|
18182
|
+
if (meta) {
|
|
18183
|
+
if (typeof meta.revertReason === "string" && meta.revertReason) {
|
|
18184
|
+
reasons.push(meta.revertReason);
|
|
18185
|
+
}
|
|
18186
|
+
if (typeof meta.failureReason === "string" && meta.failureReason) {
|
|
18187
|
+
reasons.push(meta.failureReason);
|
|
18188
|
+
}
|
|
18189
|
+
const diag = meta.developerDiagnostics;
|
|
18190
|
+
if (diag) {
|
|
18191
|
+
if (typeof diag.revertReason === "string" && diag.revertReason) {
|
|
18192
|
+
reasons.push(diag.revertReason);
|
|
18193
|
+
}
|
|
18194
|
+
if (typeof diag.error === "string" && diag.error) {
|
|
18195
|
+
reasons.push(diag.error);
|
|
18196
|
+
}
|
|
18197
|
+
const simErrors = diag.simulationErrors;
|
|
18198
|
+
if (Array.isArray(simErrors)) {
|
|
18199
|
+
for (const err of simErrors) {
|
|
18200
|
+
if (typeof err.error === "string") {
|
|
18201
|
+
reasons.push(`Simulation error: ${err.error}`);
|
|
18202
|
+
}
|
|
18203
|
+
}
|
|
18204
|
+
}
|
|
18205
|
+
}
|
|
18206
|
+
}
|
|
18207
|
+
const uniqueReasons = [...new Set(reasons)];
|
|
18208
|
+
if (uniqueReasons.length > 0) {
|
|
18209
|
+
return uniqueReasons.join("; ");
|
|
18210
|
+
}
|
|
18211
|
+
return "Transaction failed on-chain (no specific reason provided)";
|
|
18212
|
+
}
|
|
18204
18213
|
async function pollTransactionStatus(txId, client, maxAttempts = 60, intervalMs = 5e3) {
|
|
18205
18214
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
18206
18215
|
try {
|
|
@@ -18217,15 +18226,17 @@ async function pollTransactionStatus(txId, client, maxAttempts = 60, intervalMs
|
|
|
18217
18226
|
};
|
|
18218
18227
|
}
|
|
18219
18228
|
if (status === "FAILED") {
|
|
18220
|
-
|
|
18221
|
-
|
|
18222
|
-
|
|
18223
|
-
|
|
18229
|
+
const failureReason = extractFailureReason(response);
|
|
18230
|
+
console.error(`[pollTransactionStatus] Transaction ${txId} failed: ${failureReason}`);
|
|
18231
|
+
const meta = response.meta;
|
|
18232
|
+
if (meta) {
|
|
18233
|
+
console.error("[pollTransactionStatus] Full meta:", JSON.stringify(meta, null, 2));
|
|
18224
18234
|
}
|
|
18225
18235
|
return {
|
|
18226
18236
|
txId,
|
|
18227
18237
|
status: "FAILED",
|
|
18228
|
-
txHash
|
|
18238
|
+
txHash,
|
|
18239
|
+
reason: failureReason
|
|
18229
18240
|
};
|
|
18230
18241
|
}
|
|
18231
18242
|
console.log(`[pollTransactionStatus] Transaction ${txId} is ${status}, attempt ${attempt + 1}/${maxAttempts}`);
|
|
@@ -18275,19 +18286,6 @@ async function sendCalls(args) {
|
|
|
18275
18286
|
keyStorageType: deps.user?.keyStorageType
|
|
18276
18287
|
});
|
|
18277
18288
|
}
|
|
18278
|
-
if (opts.preflight !== false) {
|
|
18279
|
-
console.log("[sendCalls] Running preflight estimate...");
|
|
18280
|
-
const tolerateFundingErrors = (opts.mode ?? "sponsored") !== "self";
|
|
18281
|
-
await preflightEstimate(
|
|
18282
|
-
deps.publicClient,
|
|
18283
|
-
normalizedFrom,
|
|
18284
|
-
normalizedCalls,
|
|
18285
|
-
{ tolerateFundingErrors }
|
|
18286
|
-
);
|
|
18287
|
-
console.log("[sendCalls] Preflight estimate done");
|
|
18288
|
-
} else {
|
|
18289
|
-
console.log("[sendCalls] Preflight skipped");
|
|
18290
|
-
}
|
|
18291
18289
|
console.log("[sendCalls] Building temp auth for first precheck...");
|
|
18292
18290
|
const tempAuthForPrecheck = buildTempAuth({
|
|
18293
18291
|
chainId,
|
|
@@ -18303,11 +18301,34 @@ async function sendCalls(args) {
|
|
|
18303
18301
|
auth: tempAuthForPrecheck,
|
|
18304
18302
|
calls: normalizedCalls
|
|
18305
18303
|
});
|
|
18306
|
-
console.log("[sendCalls] First precheck done
|
|
18304
|
+
console.log("[sendCalls] First precheck done:", {
|
|
18305
|
+
policyId: precheckQuote.policyId,
|
|
18306
|
+
simulationSuccess: precheckQuote.simulationSuccess,
|
|
18307
|
+
estimatedGasPerCall: precheckQuote.estimatedGasPerCall
|
|
18308
|
+
});
|
|
18307
18309
|
} catch (err) {
|
|
18308
18310
|
console.error("[sendCalls] First precheck failed:", err);
|
|
18309
18311
|
throw err instanceof Error ? err : new Error(String(err));
|
|
18310
18312
|
}
|
|
18313
|
+
if (precheckQuote.simulationSuccess === false && precheckQuote.simulationErrors?.length) {
|
|
18314
|
+
const errors = precheckQuote.simulationErrors;
|
|
18315
|
+
console.error("[sendCalls] Backend simulation failed:", errors);
|
|
18316
|
+
const errorMessages = errors.map(
|
|
18317
|
+
(e) => `Call #${e.index}: ${e.error}${e.revertData ? ` (data: ${e.revertData})` : ""}`
|
|
18318
|
+
).join("; ");
|
|
18319
|
+
throw new Error(`Transaction simulation failed: ${errorMessages}`);
|
|
18320
|
+
}
|
|
18321
|
+
if (precheckQuote.estimatedGasPerCall?.length) {
|
|
18322
|
+
console.log("[sendCalls] Applying estimated gas limits from backend...");
|
|
18323
|
+
for (let i = 0; i < normalizedCalls.length && i < precheckQuote.estimatedGasPerCall.length; i++) {
|
|
18324
|
+
const estimatedGas = BigInt(precheckQuote.estimatedGasPerCall[i] ?? "0");
|
|
18325
|
+
const currentGas = normalizedCalls[i]?.gasLimit ?? 0n;
|
|
18326
|
+
if (estimatedGas > currentGas) {
|
|
18327
|
+
console.log(`[sendCalls] Call #${i}: updating gasLimit from ${currentGas} to ${estimatedGas}`);
|
|
18328
|
+
normalizedCalls[i].gasLimit = estimatedGas;
|
|
18329
|
+
}
|
|
18330
|
+
}
|
|
18331
|
+
}
|
|
18311
18332
|
const quotePolicyId = precheckQuote.policyId;
|
|
18312
18333
|
if (!quotePolicyId) {
|
|
18313
18334
|
throw new Error("Backend did not return policyId in precheck response");
|
|
@@ -18320,6 +18341,7 @@ async function sendCalls(args) {
|
|
|
18320
18341
|
from: normalizedFrom,
|
|
18321
18342
|
policyId: projectPolicyId,
|
|
18322
18343
|
calls: normalizedCalls,
|
|
18344
|
+
// Now with updated gas limits
|
|
18323
18345
|
expiresInSec: opts.expiresInSec ?? DEFAULT_EXPIRES_IN_SEC
|
|
18324
18346
|
});
|
|
18325
18347
|
let quote;
|
|
@@ -18538,6 +18560,10 @@ function useVolrLogin() {
|
|
|
18538
18560
|
setAccessToken(accessToken);
|
|
18539
18561
|
if (refreshToken) {
|
|
18540
18562
|
setRefreshToken(refreshToken);
|
|
18563
|
+
} else {
|
|
18564
|
+
console.warn(
|
|
18565
|
+
"[useVolrLogin] Access token received but refresh token is missing. Check if backend is updated to return refresh token in body."
|
|
18566
|
+
);
|
|
18541
18567
|
}
|
|
18542
18568
|
if (userFromServer) {
|
|
18543
18569
|
setUser(toVolrUser(userFromServer));
|
|
@@ -19283,6 +19309,219 @@ async function uploadBlobViaPresign(params) {
|
|
|
19283
19309
|
}
|
|
19284
19310
|
return { s3Key };
|
|
19285
19311
|
}
|
|
19312
|
+
|
|
19313
|
+
// src/utils/contract-analysis.ts
|
|
19314
|
+
var EIP7702_PREFIX = "0xef0100";
|
|
19315
|
+
async function isEIP7702Delegated(publicClient, address) {
|
|
19316
|
+
try {
|
|
19317
|
+
const code = await publicClient.getCode({ address });
|
|
19318
|
+
if (!code || code === "0x" || code.length < 8) {
|
|
19319
|
+
return false;
|
|
19320
|
+
}
|
|
19321
|
+
return code.toLowerCase().startsWith(EIP7702_PREFIX);
|
|
19322
|
+
} catch {
|
|
19323
|
+
return false;
|
|
19324
|
+
}
|
|
19325
|
+
}
|
|
19326
|
+
async function analyzeContractForEIP7702(publicClient, target, calldata) {
|
|
19327
|
+
const notes = [];
|
|
19328
|
+
let isContract = false;
|
|
19329
|
+
let codeSize = 0;
|
|
19330
|
+
let mayCheckSenderCode = false;
|
|
19331
|
+
let mayUseDelegationGuard = false;
|
|
19332
|
+
try {
|
|
19333
|
+
const code = await publicClient.getCode({ address: target });
|
|
19334
|
+
isContract = !!code && code !== "0x";
|
|
19335
|
+
codeSize = code ? (code.length - 2) / 2 : 0;
|
|
19336
|
+
if (!isContract || !code) {
|
|
19337
|
+
notes.push("Target is not a contract (EOA or empty address)");
|
|
19338
|
+
} else {
|
|
19339
|
+
const codeLower = code.toLowerCase();
|
|
19340
|
+
if (codeLower.includes("3b")) {
|
|
19341
|
+
mayCheckSenderCode = true;
|
|
19342
|
+
notes.push("Contract contains EXTCODESIZE opcode - may check msg.sender.code.length");
|
|
19343
|
+
}
|
|
19344
|
+
if (codeLower.includes("ef0100")) {
|
|
19345
|
+
mayUseDelegationGuard = true;
|
|
19346
|
+
notes.push("Contract contains EIP-7702 prefix bytes - may use DelegationGuard pattern");
|
|
19347
|
+
}
|
|
19348
|
+
}
|
|
19349
|
+
} catch (error) {
|
|
19350
|
+
notes.push(`Failed to fetch contract code: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
19351
|
+
}
|
|
19352
|
+
const functionSelector = calldata && calldata.length >= 10 ? calldata.slice(0, 10) : "0x";
|
|
19353
|
+
return {
|
|
19354
|
+
target,
|
|
19355
|
+
isContract,
|
|
19356
|
+
codeSize,
|
|
19357
|
+
mayCheckSenderCode,
|
|
19358
|
+
mayUseDelegationGuard,
|
|
19359
|
+
functionSelector,
|
|
19360
|
+
notes
|
|
19361
|
+
};
|
|
19362
|
+
}
|
|
19363
|
+
async function diagnoseTransactionFailure(publicClient, from14, target, calldata) {
|
|
19364
|
+
const recommendations = [];
|
|
19365
|
+
const fromCode = await publicClient.getCode({ address: from14 });
|
|
19366
|
+
const fromHasCode = !!fromCode && fromCode !== "0x";
|
|
19367
|
+
const fromIsDelegated = await isEIP7702Delegated(publicClient, from14);
|
|
19368
|
+
const targetAnalysis = await analyzeContractForEIP7702(publicClient, target, calldata);
|
|
19369
|
+
if (!targetAnalysis.isContract) {
|
|
19370
|
+
recommendations.push("Target is not a contract. Verify the target address is correct.");
|
|
19371
|
+
}
|
|
19372
|
+
if (targetAnalysis.mayCheckSenderCode) {
|
|
19373
|
+
recommendations.push(
|
|
19374
|
+
"Target contract may check msg.sender.code.length. This can cause issues because in EIP-7702, the sender EOA temporarily has code."
|
|
19375
|
+
);
|
|
19376
|
+
}
|
|
19377
|
+
if (targetAnalysis.mayUseDelegationGuard) {
|
|
19378
|
+
recommendations.push(
|
|
19379
|
+
"Target contract may use DelegationGuard pattern to block EIP-7702 delegated accounts. This is a security measure some contracts use. You may need to contact the contract developers or use a different approach."
|
|
19380
|
+
);
|
|
19381
|
+
}
|
|
19382
|
+
if (!fromIsDelegated && fromHasCode) {
|
|
19383
|
+
recommendations.push(
|
|
19384
|
+
"Sender address has code but is not EIP-7702 delegated. This may indicate the address is a contract, not an EOA."
|
|
19385
|
+
);
|
|
19386
|
+
}
|
|
19387
|
+
if (recommendations.length === 0) {
|
|
19388
|
+
recommendations.push(
|
|
19389
|
+
"No obvious EIP-7702 compatibility issues detected. The failure may be due to contract logic (e.g., insufficient balance, invalid parameters). Check the contract's requirements and your transaction parameters."
|
|
19390
|
+
);
|
|
19391
|
+
}
|
|
19392
|
+
return {
|
|
19393
|
+
fromAnalysis: {
|
|
19394
|
+
isDelegated: fromIsDelegated,
|
|
19395
|
+
hasCode: fromHasCode
|
|
19396
|
+
},
|
|
19397
|
+
targetAnalysis,
|
|
19398
|
+
recommendations
|
|
19399
|
+
};
|
|
19400
|
+
}
|
|
19401
|
+
|
|
19402
|
+
// src/utils/wallet-debug.ts
|
|
19403
|
+
async function getWalletState(publicClient, address) {
|
|
19404
|
+
const [balance, code, nonce] = await Promise.all([
|
|
19405
|
+
publicClient.getBalance({ address }),
|
|
19406
|
+
publicClient.getCode({ address }),
|
|
19407
|
+
publicClient.getTransactionCount({ address })
|
|
19408
|
+
]);
|
|
19409
|
+
const hasCode = !!code && code !== "0x";
|
|
19410
|
+
const codeLength = code ? (code.length - 2) / 2 : 0;
|
|
19411
|
+
const isEIP7702Delegated2 = hasCode && code.toLowerCase().startsWith("0xef0100");
|
|
19412
|
+
return {
|
|
19413
|
+
address,
|
|
19414
|
+
ethBalance: formatEther(balance),
|
|
19415
|
+
ethBalanceWei: balance,
|
|
19416
|
+
hasCode,
|
|
19417
|
+
codeLength,
|
|
19418
|
+
nonce,
|
|
19419
|
+
isEIP7702Delegated: isEIP7702Delegated2
|
|
19420
|
+
};
|
|
19421
|
+
}
|
|
19422
|
+
async function getERC20Balance(publicClient, walletAddress, tokenAddress, decimals = 18) {
|
|
19423
|
+
const balance = await publicClient.readContract({
|
|
19424
|
+
address: tokenAddress,
|
|
19425
|
+
abi: [
|
|
19426
|
+
{
|
|
19427
|
+
name: "balanceOf",
|
|
19428
|
+
type: "function",
|
|
19429
|
+
stateMutability: "view",
|
|
19430
|
+
inputs: [{ name: "account", type: "address" }],
|
|
19431
|
+
outputs: [{ type: "uint256" }]
|
|
19432
|
+
}
|
|
19433
|
+
],
|
|
19434
|
+
functionName: "balanceOf",
|
|
19435
|
+
args: [walletAddress]
|
|
19436
|
+
});
|
|
19437
|
+
return {
|
|
19438
|
+
address: walletAddress,
|
|
19439
|
+
tokenAddress,
|
|
19440
|
+
balance: formatUnits(balance, decimals),
|
|
19441
|
+
balanceRaw: balance,
|
|
19442
|
+
decimals
|
|
19443
|
+
};
|
|
19444
|
+
}
|
|
19445
|
+
async function compareWalletStates(publicClient, address1, address2) {
|
|
19446
|
+
const [wallet1, wallet2] = await Promise.all([
|
|
19447
|
+
getWalletState(publicClient, address1),
|
|
19448
|
+
getWalletState(publicClient, address2)
|
|
19449
|
+
]);
|
|
19450
|
+
const differences = [];
|
|
19451
|
+
if (wallet1.hasCode !== wallet2.hasCode) {
|
|
19452
|
+
differences.push(
|
|
19453
|
+
`Code presence differs: ${address1} has ${wallet1.hasCode ? "code" : "no code"}, ${address2} has ${wallet2.hasCode ? "code" : "no code"}`
|
|
19454
|
+
);
|
|
19455
|
+
}
|
|
19456
|
+
if (wallet1.isEIP7702Delegated !== wallet2.isEIP7702Delegated) {
|
|
19457
|
+
differences.push(
|
|
19458
|
+
`EIP-7702 delegation differs: ${address1} is ${wallet1.isEIP7702Delegated ? "" : "not "}delegated, ${address2} is ${wallet2.isEIP7702Delegated ? "" : "not "}delegated`
|
|
19459
|
+
);
|
|
19460
|
+
}
|
|
19461
|
+
const balanceDiff = wallet1.ethBalanceWei - wallet2.ethBalanceWei;
|
|
19462
|
+
if (balanceDiff !== 0n) {
|
|
19463
|
+
differences.push(
|
|
19464
|
+
`ETH balance differs by ${formatEther(balanceDiff > 0n ? balanceDiff : -balanceDiff)} ETH`
|
|
19465
|
+
);
|
|
19466
|
+
}
|
|
19467
|
+
return { wallet1, wallet2, differences };
|
|
19468
|
+
}
|
|
19469
|
+
async function compareERC20Balances(publicClient, address1, address2, tokenAddress, decimals = 18) {
|
|
19470
|
+
const [wallet1, wallet2] = await Promise.all([
|
|
19471
|
+
getERC20Balance(publicClient, address1, tokenAddress, decimals),
|
|
19472
|
+
getERC20Balance(publicClient, address2, tokenAddress, decimals)
|
|
19473
|
+
]);
|
|
19474
|
+
const differenceRaw = wallet1.balanceRaw - wallet2.balanceRaw;
|
|
19475
|
+
return {
|
|
19476
|
+
wallet1,
|
|
19477
|
+
wallet2,
|
|
19478
|
+
difference: formatUnits(differenceRaw > 0n ? differenceRaw : -differenceRaw, decimals),
|
|
19479
|
+
differenceRaw
|
|
19480
|
+
};
|
|
19481
|
+
}
|
|
19482
|
+
async function debugTransactionFailure(publicClient, failingWallet, workingWallet, tokenAddress, tokenDecimals = 18) {
|
|
19483
|
+
const analysis = [];
|
|
19484
|
+
const stateComparison = await compareWalletStates(
|
|
19485
|
+
publicClient,
|
|
19486
|
+
failingWallet,
|
|
19487
|
+
workingWallet
|
|
19488
|
+
);
|
|
19489
|
+
if (stateComparison.wallet1.isEIP7702Delegated && !stateComparison.wallet2.isEIP7702Delegated) {
|
|
19490
|
+
analysis.push(
|
|
19491
|
+
"The failing wallet has EIP-7702 delegation while the working wallet does not. This may cause issues if the target contract checks msg.sender.code.length or uses DelegationGuard."
|
|
19492
|
+
);
|
|
19493
|
+
}
|
|
19494
|
+
if (stateComparison.wallet1.hasCode && !stateComparison.wallet2.hasCode) {
|
|
19495
|
+
analysis.push(
|
|
19496
|
+
"The failing wallet has code while the working wallet is a pure EOA. Some contracts reject calls from addresses with code."
|
|
19497
|
+
);
|
|
19498
|
+
}
|
|
19499
|
+
let tokenComparison;
|
|
19500
|
+
if (tokenAddress) {
|
|
19501
|
+
tokenComparison = await compareERC20Balances(
|
|
19502
|
+
publicClient,
|
|
19503
|
+
failingWallet,
|
|
19504
|
+
workingWallet,
|
|
19505
|
+
tokenAddress,
|
|
19506
|
+
tokenDecimals
|
|
19507
|
+
);
|
|
19508
|
+
if (tokenComparison.wallet1.balanceRaw < tokenComparison.wallet2.balanceRaw) {
|
|
19509
|
+
analysis.push(
|
|
19510
|
+
`The failing wallet has less token balance (${tokenComparison.wallet1.balance}) than the working wallet (${tokenComparison.wallet2.balance}). Ensure sufficient balance for the transaction.`
|
|
19511
|
+
);
|
|
19512
|
+
}
|
|
19513
|
+
}
|
|
19514
|
+
if (analysis.length === 0) {
|
|
19515
|
+
analysis.push(
|
|
19516
|
+
"No obvious differences found between wallets. The issue may be related to contract-specific state (e.g., positions, allowances, whitelists) or transaction parameters."
|
|
19517
|
+
);
|
|
19518
|
+
}
|
|
19519
|
+
return {
|
|
19520
|
+
stateComparison,
|
|
19521
|
+
tokenComparison,
|
|
19522
|
+
analysis
|
|
19523
|
+
};
|
|
19524
|
+
}
|
|
19286
19525
|
/*! Bundled license information:
|
|
19287
19526
|
|
|
19288
19527
|
@noble/hashes/esm/utils.js:
|
|
@@ -19297,6 +19536,6 @@ async function uploadBlobViaPresign(params) {
|
|
|
19297
19536
|
(*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
19298
19537
|
*/
|
|
19299
19538
|
|
|
19300
|
-
export { DEFAULT_EXPIRES_IN_SEC, DEFAULT_MODE, VolrProvider, buildCall, buildCalls, createGetNetworkInfo, createPasskeyAdapter, defaultIdempotencyKey, normalizeHex, normalizeHexArray, uploadBlobViaPresign, useDepositListener, useInternalAuth, useMpcConnection, usePasskeyEnrollment, usePrecheck, useRelay, useVolr, useVolrAuthCallback, useVolrLogin, useVolrWallet };
|
|
19539
|
+
export { DEFAULT_EXPIRES_IN_SEC, DEFAULT_MODE, VolrProvider, analyzeContractForEIP7702, buildCall, buildCalls, compareERC20Balances, compareWalletStates, createGetNetworkInfo, createPasskeyAdapter, debugTransactionFailure, defaultIdempotencyKey, diagnoseTransactionFailure, getERC20Balance, getWalletState, isEIP7702Delegated, normalizeHex, normalizeHexArray, uploadBlobViaPresign, useDepositListener, useInternalAuth, useMpcConnection, usePasskeyEnrollment, usePrecheck, useRelay, useVolr, useVolrAuthCallback, useVolrLogin, useVolrWallet };
|
|
19301
19540
|
//# sourceMappingURL=index.js.map
|
|
19302
19541
|
//# sourceMappingURL=index.js.map
|