@rhinestone/deposit-modal 0.1.24 → 0.1.26

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 CHANGED
@@ -15,14 +15,10 @@ Prefer subpath imports so apps only bundle what they use:
15
15
  ```tsx
16
16
  import { DepositModal } from "@rhinestone/deposit-modal/deposit";
17
17
  import { WithdrawModal } from "@rhinestone/deposit-modal/withdraw";
18
- import { DepositModal as DepositModalReown } from "@rhinestone/deposit-modal/reown";
19
18
  import { executeSafeEthTransfer } from "@rhinestone/deposit-modal/safe";
20
19
  import { getChainName } from "@rhinestone/deposit-modal/constants";
21
20
  ```
22
21
 
23
- If you use `reownAppId`, import modal components from
24
- `@rhinestone/deposit-modal/reown`.
25
-
26
22
  ## DepositModal
27
23
 
28
24
  Deposits tokens from any supported chain into a target chain/token via a Rhinestone smart account.
@@ -92,7 +88,7 @@ Notes:
92
88
  | `defaultAmount` | `string` | No | Pre-filled amount |
93
89
  | `recipient` | `Address` | No | Custom recipient address |
94
90
  | `backendUrl` | `string` | No | Backend URL |
95
- | `rhinestoneApiKey` | `string` | No | Deprecated in client flow (server-side account preparation now owns SDK access) |
91
+ | `rhinestoneApiKey` | `string` | No | Optional SDK api key forwarded to `RhinestoneSDK` during smart-account setup |
96
92
  | `signerAddress` | `Address` | No | Session signer address |
97
93
  | `waitForFinalTx` | `boolean` | No | Wait for destination tx (default: true) |
98
94
  | `onRequestConnect` | `() => void` | No | Called when wallet connection needed |
@@ -0,0 +1,33 @@
1
+ import {
2
+ DepositModalInner
3
+ } from "./chunk-W42B54IA.mjs";
4
+ import {
5
+ ReownWalletProvider,
6
+ useReownWallet
7
+ } from "./chunk-LBEP3A2Z.mjs";
8
+ import "./chunk-3FK5FAUL.mjs";
9
+ import "./chunk-A6QLADED.mjs";
10
+
11
+ // src/DepositModalReown.tsx
12
+ import { useCallback } from "react";
13
+ import { jsx } from "react/jsx-runtime";
14
+ function DepositModalWithReown(props) {
15
+ const reown = useReownWallet();
16
+ const handleConnect = useCallback(() => {
17
+ reown.openConnect();
18
+ }, [reown.openConnect]);
19
+ return /* @__PURE__ */ jsx(
20
+ DepositModalInner,
21
+ {
22
+ ...props,
23
+ reownWallet: reown,
24
+ onConnect: handleConnect
25
+ }
26
+ );
27
+ }
28
+ function DepositModalReown(props) {
29
+ return /* @__PURE__ */ jsx(ReownWalletProvider, { projectId: props.reownAppId, theme: props.theme, children: /* @__PURE__ */ jsx(DepositModalWithReown, { ...props }) });
30
+ }
31
+ export {
32
+ DepositModalReown
33
+ };
@@ -0,0 +1,33 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+ var _chunk7QCFFKB5cjs = require('./chunk-7QCFFKB5.cjs');
4
+
5
+
6
+
7
+ var _chunk6VJ2ZTNQcjs = require('./chunk-6VJ2ZTNQ.cjs');
8
+ require('./chunk-4Q6QCALP.cjs');
9
+ require('./chunk-CEIWN53N.cjs');
10
+
11
+ // src/DepositModalReown.tsx
12
+ var _react = require('react');
13
+ var _jsxruntime = require('react/jsx-runtime');
14
+ function DepositModalWithReown(props) {
15
+ const reown = _chunk6VJ2ZTNQcjs.useReownWallet.call(void 0, );
16
+ const handleConnect = _react.useCallback.call(void 0, () => {
17
+ reown.openConnect();
18
+ }, [reown.openConnect]);
19
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
20
+ _chunk7QCFFKB5cjs.DepositModalInner,
21
+ {
22
+ ...props,
23
+ reownWallet: reown,
24
+ onConnect: handleConnect
25
+ }
26
+ );
27
+ }
28
+ function DepositModalReown(props) {
29
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6VJ2ZTNQcjs.ReownWalletProvider, { projectId: props.reownAppId, theme: props.theme, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, DepositModalWithReown, { ...props }) });
30
+ }
31
+
32
+
33
+ exports.DepositModalReown = DepositModalReown;
@@ -0,0 +1,33 @@
1
+ import {
2
+ WithdrawModalInner
3
+ } from "./chunk-DJAUNNEW.mjs";
4
+ import {
5
+ ReownWalletProvider,
6
+ useReownWallet
7
+ } from "./chunk-LBEP3A2Z.mjs";
8
+ import "./chunk-3FK5FAUL.mjs";
9
+ import "./chunk-A6QLADED.mjs";
10
+
11
+ // src/WithdrawModalReown.tsx
12
+ import { useCallback } from "react";
13
+ import { jsx } from "react/jsx-runtime";
14
+ function WithdrawModalWithReown(props) {
15
+ const reown = useReownWallet();
16
+ const handleConnect = useCallback(() => {
17
+ reown.openConnect();
18
+ }, [reown.openConnect]);
19
+ return /* @__PURE__ */ jsx(
20
+ WithdrawModalInner,
21
+ {
22
+ ...props,
23
+ reownWallet: reown,
24
+ onConnect: handleConnect
25
+ }
26
+ );
27
+ }
28
+ function WithdrawModalReown(props) {
29
+ return /* @__PURE__ */ jsx(ReownWalletProvider, { projectId: props.reownAppId, theme: props.theme, children: /* @__PURE__ */ jsx(WithdrawModalWithReown, { ...props }) });
30
+ }
31
+ export {
32
+ WithdrawModalReown
33
+ };
@@ -0,0 +1,33 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+ var _chunk4WULBRUAcjs = require('./chunk-4WULBRUA.cjs');
4
+
5
+
6
+
7
+ var _chunk6VJ2ZTNQcjs = require('./chunk-6VJ2ZTNQ.cjs');
8
+ require('./chunk-4Q6QCALP.cjs');
9
+ require('./chunk-CEIWN53N.cjs');
10
+
11
+ // src/WithdrawModalReown.tsx
12
+ var _react = require('react');
13
+ var _jsxruntime = require('react/jsx-runtime');
14
+ function WithdrawModalWithReown(props) {
15
+ const reown = _chunk6VJ2ZTNQcjs.useReownWallet.call(void 0, );
16
+ const handleConnect = _react.useCallback.call(void 0, () => {
17
+ reown.openConnect();
18
+ }, [reown.openConnect]);
19
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
20
+ _chunk4WULBRUAcjs.WithdrawModalInner,
21
+ {
22
+ ...props,
23
+ reownWallet: reown,
24
+ onConnect: handleConnect
25
+ }
26
+ );
27
+ }
28
+ function WithdrawModalReown(props) {
29
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6VJ2ZTNQcjs.ReownWalletProvider, { projectId: props.reownAppId, theme: props.theme, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, WithdrawModalWithReown, { ...props }) });
30
+ }
31
+
32
+
33
+ exports.WithdrawModalReown = WithdrawModalReown;
@@ -1,9 +1,11 @@
1
1
  import {
2
2
  CHAIN_BY_ID,
3
3
  NATIVE_TOKEN_ADDRESS,
4
+ SUPPORTED_CHAINS,
4
5
  getChainIcon,
5
6
  getChainName,
6
7
  getExplorerTxUrl,
8
+ getSupportedTokenSymbolsForChain,
7
9
  getTokenAddress,
8
10
  getTokenDecimalsByAddress,
9
11
  getTokenSymbol,
@@ -155,7 +157,7 @@ function createDepositService(baseUrl) {
155
157
  const response = await fetch(apiUrl("/prepare-account"), {
156
158
  method: "POST",
157
159
  headers: { "Content-Type": "application/json" },
158
- body: JSON.stringify(params, jsonReplacer)
160
+ body: JSON.stringify(params)
159
161
  });
160
162
  if (!response.ok) {
161
163
  const error = await response.json().catch(() => ({ error: "Unknown error" }));
@@ -163,8 +165,7 @@ function createDepositService(baseUrl) {
163
165
  error.error || `Prepare account failed: ${response.status}`
164
166
  );
165
167
  }
166
- const payload = await response.json();
167
- return normalizePrepareAccountResponse(payload);
168
+ return response.json();
168
169
  },
169
170
  async registerAccount(params) {
170
171
  const { eoaAddress, sessionOwner, ...account } = params;
@@ -246,58 +247,40 @@ function createDepositService(baseUrl) {
246
247
  return { lastEvent: void 0 };
247
248
  }
248
249
  return response.json();
249
- }
250
- };
251
- }
252
- function buildSessionDetails(sessionDetailsUnsigned, signature) {
253
- return {
254
- hashesAndChainIds: sessionDetailsUnsigned.hashesAndChainIds,
255
- signature
256
- };
257
- }
258
- function normalizePrepareAccountResponse(payload) {
259
- if (!isRecord(payload)) {
260
- throw new Error("Invalid prepare-account response");
261
- }
262
- const smartAccount = extractString(payload, "smartAccount");
263
- const accountParams = isRecord(payload.accountParams) ? payload.accountParams : null;
264
- const sessionDetailsUnsigned = isRecord(payload.sessionDetailsUnsigned) ? payload.sessionDetailsUnsigned : null;
265
- if (!smartAccount || !accountParams || !sessionDetailsUnsigned) {
266
- throw new Error("Missing prepare-account fields");
267
- }
268
- const factory = extractString(accountParams, "factory");
269
- const factoryData = extractString(accountParams, "factoryData");
270
- if (!factory || !factoryData) {
271
- throw new Error("Invalid account params from prepare-account");
272
- }
273
- const rawHashes = Array.isArray(sessionDetailsUnsigned.hashesAndChainIds) ? sessionDetailsUnsigned.hashesAndChainIds : null;
274
- if (!rawHashes) {
275
- throw new Error("Missing session hashes from prepare-account");
276
- }
277
- const hashesAndChainIds = rawHashes.map((value) => {
278
- if (!isRecord(value)) return null;
279
- const sessionDigest = extractString(value, "sessionDigest");
280
- const chainId = toBigInt(value.chainId);
281
- if (!sessionDigest || chainId === null) return null;
282
- return {
283
- chainId,
284
- sessionDigest
285
- };
286
- }).filter(
287
- (item) => item !== null
288
- );
289
- if (hashesAndChainIds.length === 0) {
290
- throw new Error("Session hashes are empty");
291
- }
292
- return {
293
- smartAccount,
294
- accountParams: {
295
- factory,
296
- factoryData
297
250
  },
298
- sessionDetailsUnsigned: {
299
- hashesAndChainIds,
300
- data: sessionDetailsUnsigned.data
251
+ async relayWithdraw(params) {
252
+ const { smartAccount, chainId, safeAddress, safeTransaction, signature } = params;
253
+ const response = await fetch(apiUrl(`/relay-withdraw`), {
254
+ method: "POST",
255
+ headers: { "Content-Type": "application/json" },
256
+ body: JSON.stringify(
257
+ {
258
+ chainId,
259
+ safeAddress,
260
+ safeTransaction: {
261
+ to: safeTransaction.to,
262
+ value: safeTransaction.value.toString(),
263
+ data: safeTransaction.data,
264
+ operation: safeTransaction.operation,
265
+ safeTxGas: safeTransaction.safeTxGas.toString(),
266
+ baseGas: safeTransaction.baseGas.toString(),
267
+ gasPrice: safeTransaction.gasPrice.toString(),
268
+ gasToken: safeTransaction.gasToken,
269
+ refundReceiver: safeTransaction.refundReceiver,
270
+ nonce: safeTransaction.nonce.toString()
271
+ },
272
+ signature
273
+ },
274
+ jsonReplacer
275
+ )
276
+ });
277
+ if (!response.ok) {
278
+ const error = await response.json().catch(() => ({ error: "Unknown error" }));
279
+ throw new Error(
280
+ error.error || `Relay withdraw failed: ${response.status}`
281
+ );
282
+ }
283
+ return response.json();
301
284
  }
302
285
  };
303
286
  }
@@ -386,23 +369,6 @@ function extractTokenAddress(tokenData, chainId) {
386
369
  const token = tokenData;
387
370
  return token.tokenAddress ?? token.address ?? token.addresses?.[chainId] ?? token.token?.address ?? token.token?.addresses?.[chainId];
388
371
  }
389
- function isRecord(value) {
390
- return typeof value === "object" && value !== null;
391
- }
392
- function toBigInt(value) {
393
- if (typeof value === "bigint") return value;
394
- if (typeof value === "number" && Number.isInteger(value) && value >= 0) {
395
- return BigInt(value);
396
- }
397
- if (typeof value === "string" && /^\d+$/.test(value)) {
398
- try {
399
- return BigInt(value);
400
- } catch {
401
- return null;
402
- }
403
- }
404
- return null;
405
- }
406
372
  function extractArray(data, key) {
407
373
  if (!data || typeof data !== "object") return null;
408
374
  const record = data;
@@ -851,6 +817,203 @@ function ConnectStep({
851
817
  }
852
818
  ConnectStep.displayName = "ConnectStep";
853
819
 
820
+ // src/core/account.ts
821
+ import {
822
+ RhinestoneSDK
823
+ } from "@rhinestone/sdk";
824
+ import { zeroAddress } from "viem";
825
+ import { toAccount } from "viem/accounts";
826
+ function createViewOnlyAccount(address) {
827
+ if (!address || address === zeroAddress) {
828
+ throw new Error("Address is required");
829
+ }
830
+ return toAccount({
831
+ address,
832
+ signMessage: async () => {
833
+ throw new Error("Account is view-only");
834
+ },
835
+ signTransaction: async () => {
836
+ throw new Error("Account is view-only");
837
+ },
838
+ signTypedData: async () => {
839
+ throw new Error("Account is view-only");
840
+ }
841
+ });
842
+ }
843
+ function getSessionSignerAccount(signerAddress) {
844
+ if (!signerAddress || signerAddress === zeroAddress) {
845
+ throw new Error("Signer address is required");
846
+ }
847
+ return toAccount({
848
+ address: signerAddress,
849
+ signMessage: async () => {
850
+ throw new Error("Session signer is view-only");
851
+ },
852
+ signTransaction: async () => {
853
+ throw new Error("Session signer is view-only");
854
+ },
855
+ signTypedData: async () => {
856
+ throw new Error("Session signer is view-only");
857
+ }
858
+ });
859
+ }
860
+ function buildSession(chain, signerAddress) {
861
+ const sessionSignerAccount = getSessionSignerAccount(signerAddress);
862
+ return {
863
+ owners: {
864
+ type: "ecdsa",
865
+ accounts: [sessionSignerAccount]
866
+ },
867
+ chain
868
+ };
869
+ }
870
+ async function createSmartAccount(userSigner, sessionSigner, sdkApiKey) {
871
+ const rhinestone = new RhinestoneSDK({
872
+ apiKey: sdkApiKey ?? ""
873
+ });
874
+ const ownerAccounts = sessionSigner ? [userSigner, sessionSigner] : [userSigner];
875
+ const config = {
876
+ account: {
877
+ type: "nexus"
878
+ },
879
+ owners: {
880
+ type: "ecdsa",
881
+ accounts: ownerAccounts,
882
+ ...sessionSigner ? { threshold: 1 } : {}
883
+ },
884
+ experimental_sessions: {
885
+ enabled: true
886
+ }
887
+ };
888
+ const account = await rhinestone.createAccount(config);
889
+ return account;
890
+ }
891
+ function getAccountAddress(account) {
892
+ return account.getAddress();
893
+ }
894
+ function getAccountInitData(account) {
895
+ const { factory, factoryData } = account.getInitData();
896
+ if (!factory) {
897
+ throw new Error("Account init data is not available");
898
+ }
899
+ return {
900
+ factory,
901
+ factoryData
902
+ };
903
+ }
904
+ async function signEnableSessionWithOwner(rhinestoneAccount, sessionDetails, signer) {
905
+ const originalOwners = rhinestoneAccount.config.owners;
906
+ rhinestoneAccount.config.owners = {
907
+ type: "ecdsa",
908
+ accounts: [signer],
909
+ threshold: 1,
910
+ ...originalOwners?.type === "ecdsa" && originalOwners.module ? { module: originalOwners.module } : {}
911
+ };
912
+ try {
913
+ return await rhinestoneAccount.experimental_signEnableSession(
914
+ sessionDetails
915
+ );
916
+ } finally {
917
+ rhinestoneAccount.config.owners = originalOwners;
918
+ }
919
+ }
920
+ async function getSessionDetails(rhinestoneAccount, targetChain, signerAddress, sessionSigner, targetToken, sessionChainIds) {
921
+ const isTargetTestnet = Boolean(targetChain.testnet);
922
+ const chainsByNetworkType = SUPPORTED_CHAINS.filter(
923
+ (chain) => Boolean(chain.testnet) === isTargetTestnet
924
+ );
925
+ const chainsById = new Map(
926
+ chainsByNetworkType.map((chain) => [chain.id, chain])
927
+ );
928
+ let selectedChains = [...chainsByNetworkType];
929
+ if (sessionChainIds && sessionChainIds.length > 0) {
930
+ const selectedByCaller = [];
931
+ for (const chainId of sessionChainIds) {
932
+ const chain = chainsById.get(chainId);
933
+ if (chain) {
934
+ selectedByCaller.push(chain);
935
+ }
936
+ }
937
+ if (selectedByCaller.length > 0) {
938
+ selectedChains = selectedByCaller;
939
+ }
940
+ }
941
+ if (targetToken) {
942
+ const targetSymbol = getTokenSymbol(
943
+ targetToken,
944
+ targetChain.id
945
+ ).toUpperCase();
946
+ if (targetSymbol && targetSymbol !== "TOKEN") {
947
+ const chainsForToken = selectedChains.filter(
948
+ (chain) => getSupportedTokenSymbolsForChain(chain.id).includes(targetSymbol)
949
+ );
950
+ if (chainsForToken.length > 0) {
951
+ selectedChains = chainsForToken;
952
+ }
953
+ }
954
+ }
955
+ if (!selectedChains.some((c) => c.id === targetChain.id)) {
956
+ selectedChains.push(targetChain);
957
+ }
958
+ const uniqueChains = Array.from(
959
+ new Map(selectedChains.map((chain) => [chain.id, chain])).values()
960
+ );
961
+ const sessions = uniqueChains.map(
962
+ (chain) => buildSession(chain, signerAddress)
963
+ );
964
+ const sessionDetails = await rhinestoneAccount.experimental_getSessionDetails(sessions);
965
+ const enableSignature = sessionSigner ? await signEnableSessionWithOwner(
966
+ rhinestoneAccount,
967
+ sessionDetails,
968
+ sessionSigner
969
+ ) : await rhinestoneAccount.experimental_signEnableSession(sessionDetails);
970
+ if (!sessionDetails.hashesAndChainIds?.length) {
971
+ throw new Error("Session details missing chain digests");
972
+ }
973
+ return {
974
+ hashesAndChainIds: sessionDetails.hashesAndChainIds,
975
+ signature: enableSignature
976
+ };
977
+ }
978
+ function deserializeSessionData(raw) {
979
+ const data = structuredClone(raw);
980
+ const message = data.message;
981
+ if (!message?.sessionsAndChainIds) return data;
982
+ for (const entry of message.sessionsAndChainIds) {
983
+ const chainSession = entry;
984
+ chainSession.chainId = BigInt(chainSession.chainId);
985
+ const session = chainSession.session;
986
+ if (session) {
987
+ session.nonce = BigInt(session.nonce);
988
+ session.expires = BigInt(session.expires);
989
+ }
990
+ }
991
+ return data;
992
+ }
993
+ async function signSessionDetails(rhinestoneAccount, sessionDetailsUnsigned, signer) {
994
+ const data = deserializeSessionData(sessionDetailsUnsigned.data);
995
+ const hashesAndChainIds = sessionDetailsUnsigned.hashesAndChainIds.map(
996
+ (h) => ({
997
+ chainId: BigInt(h.chainId),
998
+ sessionDigest: h.sessionDigest
999
+ })
1000
+ );
1001
+ const sessionDetails = {
1002
+ nonces: [],
1003
+ hashesAndChainIds,
1004
+ data
1005
+ };
1006
+ const signature = await signEnableSessionWithOwner(
1007
+ rhinestoneAccount,
1008
+ sessionDetails,
1009
+ signer
1010
+ );
1011
+ return {
1012
+ hashesAndChainIds,
1013
+ signature
1014
+ };
1015
+ }
1016
+
854
1017
  // src/core/session-owner.ts
855
1018
  import { isAddress } from "viem";
856
1019
  import {
@@ -942,7 +1105,7 @@ function PoweredBy() {
942
1105
  }
943
1106
 
944
1107
  // src/core/webhook.ts
945
- function isRecord2(value) {
1108
+ function isRecord(value) {
946
1109
  return typeof value === "object" && value !== null;
947
1110
  }
948
1111
  function asString(value) {
@@ -954,11 +1117,11 @@ function getEventTxHash(event) {
954
1117
  return asString(event.data?.transactionHash);
955
1118
  }
956
1119
  if (event.type === "bridge-started" || event.type === "bridge-complete") {
957
- const deposit = isRecord2(event.data?.deposit) ? event.data.deposit : void 0;
1120
+ const deposit = isRecord(event.data?.deposit) ? event.data.deposit : void 0;
958
1121
  return asString(deposit?.transactionHash);
959
1122
  }
960
1123
  if (event.type === "bridge-failed" || event.type === "error") {
961
- const deposit = isRecord2(event.data?.deposit) ? event.data.deposit : void 0;
1124
+ const deposit = isRecord(event.data?.deposit) ? event.data.deposit : void 0;
962
1125
  return asString(deposit?.transactionHash);
963
1126
  }
964
1127
  return void 0;
@@ -1546,16 +1709,21 @@ export {
1546
1709
  Spinner,
1547
1710
  Button,
1548
1711
  ConnectStep,
1712
+ createViewOnlyAccount,
1713
+ createSmartAccount,
1714
+ getAccountAddress,
1715
+ getAccountInitData,
1716
+ getSessionDetails,
1717
+ signSessionDetails,
1549
1718
  loadSessionOwnerFromStorage,
1550
1719
  saveSessionOwnerToStorage,
1551
1720
  createSessionOwnerKey,
1552
1721
  accountFromPrivateKey,
1722
+ PoweredBy,
1553
1723
  createDepositService,
1554
- buildSessionDetails,
1555
1724
  getAssetId,
1556
1725
  portfolioToAssets,
1557
1726
  isNativeAsset,
1558
- PoweredBy,
1559
1727
  currencyFormatter,
1560
1728
  tokenFormatter,
1561
1729
  formatUserError,