0xtrails 0.2.5 → 0.2.6

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.
Files changed (116) hide show
  1. package/dist/abortController.d.ts +8 -0
  2. package/dist/abortController.d.ts.map +1 -0
  3. package/dist/{ccip-CXlshvBY.js → ccip-Xjh9d1gb.js} +7 -7
  4. package/dist/constants.d.ts +2 -0
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/fees.d.ts +19 -0
  7. package/dist/fees.d.ts.map +1 -0
  8. package/dist/{index-_QuyGrjU.js → index-BnhdZ8Ho.js} +34769 -34247
  9. package/dist/index.js +726 -520
  10. package/dist/prepareSend.d.ts +11 -77
  11. package/dist/prepareSend.d.ts.map +1 -1
  12. package/dist/transactions.d.ts +4 -2
  13. package/dist/transactions.d.ts.map +1 -1
  14. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts +4 -0
  15. package/dist/widget/components/AccountIntentTransactionHistoryButton.d.ts.map +1 -0
  16. package/dist/widget/components/AccountSettings.d.ts.map +1 -1
  17. package/dist/widget/components/ChainFilterDropdown.d.ts.map +1 -1
  18. package/dist/widget/components/ClassicSwap.d.ts +2 -2
  19. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  20. package/dist/widget/components/ConnectWallet.d.ts.map +1 -1
  21. package/dist/widget/components/DynamicInputStyles.d.ts +18 -0
  22. package/dist/widget/components/DynamicInputStyles.d.ts.map +1 -0
  23. package/dist/widget/components/Earn.d.ts +2 -2
  24. package/dist/widget/components/Earn.d.ts.map +1 -1
  25. package/dist/widget/components/ErrorAnimationIcon.d.ts +2 -0
  26. package/dist/widget/components/ErrorAnimationIcon.d.ts.map +1 -0
  27. package/dist/widget/components/FeeBreakdown.d.ts +9 -0
  28. package/dist/widget/components/FeeBreakdown.d.ts.map +1 -0
  29. package/dist/widget/components/Fund.d.ts +2 -2
  30. package/dist/widget/components/Fund.d.ts.map +1 -1
  31. package/dist/widget/components/FundSwap.d.ts +2 -2
  32. package/dist/widget/components/FundSwap.d.ts.map +1 -1
  33. package/dist/widget/components/FundingMethodSelectorButton.d.ts.map +1 -1
  34. package/dist/widget/components/Identicon.d.ts.map +1 -1
  35. package/dist/widget/components/MeshConnectExchanges.d.ts +0 -3
  36. package/dist/widget/components/MeshConnectExchanges.d.ts.map +1 -1
  37. package/dist/widget/components/Modal.d.ts.map +1 -1
  38. package/dist/widget/components/Pay.d.ts +2 -2
  39. package/dist/widget/components/Pay.d.ts.map +1 -1
  40. package/dist/widget/components/PoolDeposit.d.ts +3 -2
  41. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  42. package/dist/widget/components/PoolWithdraw.d.ts +3 -2
  43. package/dist/widget/components/PoolWithdraw.d.ts.map +1 -1
  44. package/dist/widget/components/QuoteDetails.d.ts +1 -0
  45. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  46. package/dist/widget/components/Receipt.d.ts.map +1 -1
  47. package/dist/widget/components/RecipientSelectorButton.d.ts.map +1 -1
  48. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  49. package/dist/widget/components/Swap.d.ts +2 -2
  50. package/dist/widget/components/Swap.d.ts.map +1 -1
  51. package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
  52. package/dist/widget/components/TokenDisplayNonSelectable.d.ts +11 -0
  53. package/dist/widget/components/TokenDisplayNonSelectable.d.ts.map +1 -0
  54. package/dist/widget/components/TokenSelector.d.ts.map +1 -1
  55. package/dist/widget/components/TokenSelectorButton.d.ts.map +1 -1
  56. package/dist/widget/components/Tooltip.d.ts +9 -0
  57. package/dist/widget/components/Tooltip.d.ts.map +1 -0
  58. package/dist/widget/components/WaasFeeOptions.d.ts +1 -0
  59. package/dist/widget/components/WaasFeeOptions.d.ts.map +1 -1
  60. package/dist/widget/components/WalletConfirmation.d.ts.map +1 -1
  61. package/dist/widget/components/WalletConnect.d.ts.map +1 -1
  62. package/dist/widget/css/compiled.css +1 -1
  63. package/dist/widget/hooks/useQuote.d.ts +83 -0
  64. package/dist/widget/hooks/useQuote.d.ts.map +1 -0
  65. package/dist/widget/hooks/useSendForm.d.ts +2 -2
  66. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  67. package/dist/widget/index.js +2 -2
  68. package/dist/widget/widget.d.ts +5 -0
  69. package/dist/widget/widget.d.ts.map +1 -1
  70. package/package.json +2 -2
  71. package/src/abortController.ts +35 -0
  72. package/src/constants.ts +3 -0
  73. package/src/fees.ts +199 -0
  74. package/src/prepareSend.ts +225 -398
  75. package/src/trails.ts +3 -3
  76. package/src/transactions.ts +62 -18
  77. package/src/widget/compiled.css +1 -1
  78. package/src/widget/components/AccountIntentTransactionHistoryButton.tsx +22 -0
  79. package/src/widget/components/AccountSettings.tsx +48 -36
  80. package/src/widget/components/ChainFilterDropdown.tsx +24 -3
  81. package/src/widget/components/ClassicSwap.tsx +24 -62
  82. package/src/widget/components/ConnectWallet.tsx +4 -1
  83. package/src/widget/components/ConnectedWallets.tsx +21 -21
  84. package/src/widget/components/DynamicInputStyles.tsx +76 -0
  85. package/src/widget/components/Earn.tsx +34 -29
  86. package/src/widget/components/ErrorAnimationIcon.tsx +130 -0
  87. package/src/widget/components/FeeBreakdown.tsx +155 -0
  88. package/src/widget/components/Fund.tsx +10 -26
  89. package/src/widget/components/FundSwap.tsx +2 -2
  90. package/src/widget/components/FundingMethodSelectorButton.tsx +24 -14
  91. package/src/widget/components/Identicon.tsx +164 -95
  92. package/src/widget/components/MeshConnectExchanges.tsx +2 -15
  93. package/src/widget/components/Modal.tsx +0 -12
  94. package/src/widget/components/Pay.tsx +65 -63
  95. package/src/widget/components/PoolDeposit.tsx +206 -230
  96. package/src/widget/components/PoolWithdraw.tsx +219 -238
  97. package/src/widget/components/PriceImpactWarning.tsx +1 -1
  98. package/src/widget/components/QuoteDetails.tsx +25 -8
  99. package/src/widget/components/Receipt.tsx +16 -2
  100. package/src/widget/components/RecipientSelectorButton.tsx +7 -5
  101. package/src/widget/components/Recipients.tsx +1 -1
  102. package/src/widget/components/ScreenHeader.tsx +60 -36
  103. package/src/widget/components/Swap.tsx +2 -2
  104. package/src/widget/components/ThemeProvider.tsx +2 -1
  105. package/src/widget/components/TokenDisplayNonSelectable.tsx +40 -0
  106. package/src/widget/components/TokenImage.tsx +1 -1
  107. package/src/widget/components/TokenSelector.tsx +62 -53
  108. package/src/widget/components/TokenSelectorButton.tsx +38 -15
  109. package/src/widget/components/Tooltip.tsx +51 -0
  110. package/src/widget/components/TransferPendingVertical.tsx +1 -1
  111. package/src/widget/components/WaasFeeOptions.tsx +124 -5
  112. package/src/widget/components/WalletConfirmation.tsx +23 -13
  113. package/src/widget/components/WalletConnect.tsx +93 -29
  114. package/src/widget/hooks/useQuote.ts +413 -0
  115. package/src/widget/hooks/useSendForm.ts +8 -4
  116. package/src/widget/widget.tsx +175 -190
@@ -0,0 +1,155 @@
1
+ import type React from "react"
2
+ import { useState } from "react"
3
+ import { InfoIcon } from "@0xsequence/design-system"
4
+ import { Tooltip } from "./Tooltip.js"
5
+ import { TokenImage } from "./TokenImage.js"
6
+ import type { TrailsFeeBreakdown, FeeItem } from "../../fees.js"
7
+
8
+ interface FeeBreakdownProps {
9
+ feeBreakdown: TrailsFeeBreakdown
10
+ children?: React.ReactNode
11
+ }
12
+
13
+ interface FeeRowProps {
14
+ label: string
15
+ feeItem: FeeItem
16
+ tooltip?: string
17
+ }
18
+
19
+ const FeeRow: React.FC<FeeRowProps> = ({ label, feeItem, tooltip }) => (
20
+ <div className="flex justify-between items-center py-1 items-start">
21
+ <span className="text-xs text-gray-600 dark:text-gray-400 flex items-center gap-1">
22
+ {label}
23
+ {tooltip && (
24
+ <Tooltip message={tooltip}>
25
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
26
+ </Tooltip>
27
+ )}
28
+ </span>
29
+ <div className="text-right">
30
+ <div className="font-medium text-xs text-gray-900 dark:text-white flex items-center gap-1 justify-end">
31
+ <TokenImage
32
+ imageUrl=""
33
+ symbol={feeItem.tokenSymbol}
34
+ chainId={feeItem.chainId}
35
+ contractAddress={feeItem.tokenAddress}
36
+ size={16}
37
+ />
38
+ {feeItem.amount} {feeItem.tokenSymbol}
39
+ </div>
40
+ <div className="text-xs text-gray-500 dark:text-gray-400">
41
+ ~{feeItem.usdValue}
42
+ </div>
43
+ </div>
44
+ </div>
45
+ )
46
+
47
+ export const FeeBreakdown: React.FC<FeeBreakdownProps> = ({
48
+ feeBreakdown,
49
+ children,
50
+ }) => {
51
+ const [isExpanded, setIsExpanded] = useState(false)
52
+
53
+ // Don't render at all if there are no fees
54
+ const hasAnyFees =
55
+ feeBreakdown.originRelayFee ||
56
+ feeBreakdown.destinationRelayFee ||
57
+ feeBreakdown.providerFee ||
58
+ feeBreakdown.trailsFee
59
+
60
+ if (!hasAnyFees) {
61
+ return null
62
+ }
63
+
64
+ return (
65
+ <div className="space-y-2">
66
+ {/* Accordion header */}
67
+ <button
68
+ type="button"
69
+ onClick={() => setIsExpanded(!isExpanded)}
70
+ className="w-full flex items-center justify-between py-1 text-xs transition-colors duration-200 text-gray-600 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 cursor-pointer"
71
+ aria-label={isExpanded ? "Hide fee breakdown" : "Show fee breakdown"}
72
+ >
73
+ <span className="flex items-center gap-1 w-full">
74
+ {children ? (
75
+ children
76
+ ) : (
77
+ <Tooltip message="Detailed breakdown of the Trails platform fees">
78
+ <InfoIcon className="w-3 h-3 text-gray-500 dark:text-gray-400 cursor-pointer" />
79
+ </Tooltip>
80
+ )}
81
+ </span>
82
+ <svg
83
+ className={`w-3 h-3 transition-transform duration-300 ease-out ml-2 ${
84
+ isExpanded ? "rotate-180" : ""
85
+ }`}
86
+ fill="none"
87
+ stroke="currentColor"
88
+ viewBox="0 0 24 24"
89
+ aria-hidden="true"
90
+ >
91
+ <path
92
+ strokeLinecap="round"
93
+ strokeLinejoin="round"
94
+ strokeWidth={2}
95
+ d="M19 9l-7 7-7-7"
96
+ />
97
+ </svg>
98
+ </button>
99
+
100
+ {/* Collapsible content */}
101
+ <div
102
+ className={`overflow-hidden transition-all duration-300 ease-out ${
103
+ isExpanded ? "max-h-96 opacity-100" : "max-h-0 opacity-0"
104
+ }`}
105
+ >
106
+ <div className="space-y-1 pl-2">
107
+ {feeBreakdown.originRelayFee && (
108
+ <FeeRow
109
+ label="Origin Relay Fee"
110
+ feeItem={feeBreakdown.originRelayFee}
111
+ tooltip="Fee for relaying the origin chain transaction"
112
+ />
113
+ )}
114
+ {feeBreakdown.destinationRelayFee && (
115
+ <FeeRow
116
+ label="Destination Relay Fee"
117
+ feeItem={feeBreakdown.destinationRelayFee}
118
+ tooltip="Fee for relaying the destination chain transaction"
119
+ />
120
+ )}
121
+ {feeBreakdown.providerFee && (
122
+ <FeeRow
123
+ label="Provider Fee"
124
+ feeItem={feeBreakdown.providerFee}
125
+ tooltip="Fee charged by the bridge/swap provider for executing the transaction"
126
+ />
127
+ )}
128
+ {feeBreakdown.trailsFee && (
129
+ <FeeRow
130
+ label="Trails Platform Fee"
131
+ feeItem={feeBreakdown.trailsFee}
132
+ tooltip="Platform fee for using the Trails service"
133
+ />
134
+ )}
135
+
136
+ {/* Total line - only show if we have a total value */}
137
+ {/* {feeBreakdown.totalUsdValue && (
138
+ <div className="flex justify-between items-center py-1 pt-2 border-t border-gray-200 dark:border-gray-700">
139
+ <span className="text-xs font-medium text-gray-700 dark:text-gray-300">
140
+ Total Fees
141
+ </span>
142
+ <div className="text-right">
143
+ <div className="font-medium text-xs text-gray-900 dark:text-white">
144
+ ~{feeBreakdown.totalUsdValue}
145
+ </div>
146
+ </div>
147
+ </div>
148
+ )} */}
149
+ </div>
150
+ </div>
151
+ </div>
152
+ )
153
+ }
154
+
155
+ export default FeeBreakdown
@@ -32,11 +32,12 @@ import { logger } from "../../logger.js"
32
32
  import { RefundWarning } from "./RefundWarning.js"
33
33
  import { PercentageMaxButtons } from "./PercentageMaxButtons.js"
34
34
  import { TokenSelectorButton } from "./TokenSelectorButton.js"
35
+ import { useDynamicInputStyles } from "./DynamicInputStyles.js"
35
36
 
36
37
  interface FundProps {
37
38
  onBack?: () => void
38
- account: Account
39
- walletClient: WalletClient
39
+ account?: Account
40
+ walletClient?: WalletClient
40
41
  onTransactionStateChange: (transactionStates: TransactionState[]) => void
41
42
  onError: (error: Error | string | null) => void
42
43
  onWaitingForWalletConfirm: (props: PrepareSendQuote) => void
@@ -151,7 +152,7 @@ export const Fund: React.FC<FundProps> = ({
151
152
  } = useSendForm({
152
153
  account,
153
154
  toAmount: undefined, // Don't pass toAmount for fund form - user enters input amount
154
- toRecipient: selectedRecipient || account.address,
155
+ toRecipient: selectedRecipient || account?.address,
155
156
  toChainId,
156
157
  toToken,
157
158
  toCalldata,
@@ -521,27 +522,10 @@ export const Fund: React.FC<FundProps> = ({
521
522
  }, [tokenAmountForBackend, isInputTypeUsd, sourceTokenPrice])
522
523
 
523
524
  // Dynamic font size based on input length - matching Earn.tsx
524
- const inputStyles = useMemo(() => {
525
- const inputLength = displayAmount.length
526
- let fontSize: string
527
-
528
- if (inputLength > 12) {
529
- fontSize = "0.875rem"
530
- } else if (inputLength > 9) {
531
- fontSize = "1rem"
532
- } else if (inputLength > 6) {
533
- fontSize = "1.125rem"
534
- } else if (inputLength > 3) {
535
- fontSize = "1.25rem"
536
- } else {
537
- fontSize = "1.5rem"
538
- }
539
-
540
- return {
541
- fontSize,
542
- transition: "all 0.1s ease-in-out",
543
- }
544
- }, [displayAmount.length])
525
+ const inputStyles = useDynamicInputStyles({
526
+ inputValue: displayAmount,
527
+ variant: "smaller",
528
+ })
545
529
 
546
530
  const handleOriginTokenSelect = useCallback(
547
531
  (token: any) => {
@@ -839,7 +823,7 @@ export const Fund: React.FC<FundProps> = ({
839
823
  {/* Bottom Info Row */}
840
824
  <div className="mt-2 flex justify-between items-center">
841
825
  {/* USD Amount */}
842
- <div className="text-xs trails-text-muted">
826
+ <div className="text-xs text-gray-500 dark:text-gray-400">
843
827
  {originToken?.symbol && displayAmount ? (
844
828
  <>≈ {amountUsdDisplay || "$0.00"}</>
845
829
  ) : (
@@ -852,7 +836,7 @@ export const Fund: React.FC<FundProps> = ({
852
836
  <div className="flex items-center space-x-2">
853
837
  <button
854
838
  type="button"
855
- className="text-xs trails-text-muted cursor-pointer hover:trails-hover-text transition-colors bg-transparent border-none p-0"
839
+ className="text-xs text-gray-500 dark:text-gray-400 cursor-pointer hover:text-gray-700 dark:hover:text-gray-200 transition-colors bg-transparent border-none p-0"
856
840
  onClick={() => handlePercentageClick(100)}
857
841
  onKeyDown={(e) => {
858
842
  if (e.key === "Enter" || e.key === " ") {
@@ -13,13 +13,13 @@ interface FundProps {
13
13
  onBack?: () => void
14
14
  onConfirm: () => void
15
15
  onComplete: (result: OnCompleteProps) => void
16
- account: Account
16
+ account?: Account
17
17
  toRecipient?: string
18
18
  toAmount?: string
19
19
  toChainId?: number
20
20
  toToken?: string
21
21
  toCalldata?: string
22
- walletClient: WalletClient
22
+ walletClient?: WalletClient
23
23
  onTransactionStateChange: (transactionStates: TransactionState[]) => void
24
24
  onError: (error: Error | string | null) => void
25
25
  onWaitingForWalletConfirm: (props: PrepareSendQuote) => void
@@ -1,18 +1,24 @@
1
1
  import { ChevronRight, QrCode, Send } from "lucide-react"
2
2
  import type React from "react"
3
3
  import { truncateAddress } from "../../utils.js"
4
- import { useAccount } from "wagmi"
4
+ import { useAccount, useConnections } from "wagmi"
5
5
  import { Identicon } from "./Identicon.js"
6
6
  import { useBack } from "../hooks/useBack.js"
7
7
  import { useSelectedFundMethod } from "../hooks/useSelectedFundMethod.js"
8
8
 
9
9
  export const FundingMethodSelectorButton: React.FC = () => {
10
- const { address, isConnected } = useAccount()
10
+ const connections = useConnections()
11
+ const { address } = useAccount()
11
12
  const { setCurrentScreenWithBack } = useBack()
12
13
  const { selectedFundMethod } = useSelectedFundMethod()
13
14
 
15
+ // Check if there are any connected accounts across all connectors
16
+ const isConnected = connections.length > 0
17
+ // Use the active account from useAccount(), fallback to first connection if needed
18
+ const activeAddress = address || connections[0]?.accounts[0]
19
+
14
20
  const handleClick = () => {
15
- if (isConnected && address) {
21
+ if (isConnected && activeAddress) {
16
22
  // If wallet is connected, go to fund methods
17
23
  setCurrentScreenWithBack("fund-methods")
18
24
  } else {
@@ -27,32 +33,36 @@ export const FundingMethodSelectorButton: React.FC = () => {
27
33
  onClick={handleClick}
28
34
  className="flex items-center space-x-2 text-blue-500 hover:text-blue-600 transition-colors cursor-pointer bg-transparent border-none p-0"
29
35
  title={
30
- isConnected && address ? "Select funding method" : "Connect wallet"
36
+ isConnected && activeAddress
37
+ ? "Select funding method"
38
+ : "Connect wallet"
31
39
  }
32
40
  >
33
- {isConnected && address ? (
41
+ {isConnected && activeAddress ? (
34
42
  selectedFundMethod === "qr-code" ? (
35
43
  <>
36
- <QrCode className="w-4 h-4" />
37
- <span className="text-sm font-medium">QR Code</span>
44
+ <QrCode className="w-4 h-4 mr-1" />
45
+ <span className="text-sm font-medium m-0">QR Code</span>
38
46
  </>
39
47
  ) : selectedFundMethod === "exchange" ? (
40
48
  <>
41
- <Send className="w-4 h-4" />
42
- <span className="text-sm font-medium">Exchange</span>
49
+ <Send className="w-4 h-4 mr-1" />
50
+ <span className="text-sm font-medium m-0">Exchange</span>
43
51
  </>
44
52
  ) : (
45
53
  <>
46
- <Identicon value={address} size={16} />
47
- <span className="text-sm font-medium">
48
- {truncateAddress(address, 4, 2)}
54
+ <div className="flex items-center mr-1">
55
+ <Identicon value={activeAddress} size={16} />
56
+ </div>
57
+ <span className="text-sm font-medium m-0">
58
+ {truncateAddress(activeAddress, 4, 4)}
49
59
  </span>
50
60
  </>
51
61
  )
52
62
  ) : (
53
- <span className="text-sm font-medium">Connect Wallet</span>
63
+ <span className="text-sm font-medium mr-1">Connect Wallet</span>
54
64
  )}
55
- <ChevronRight className="w-4 h-4" />
65
+ <ChevronRight className="w-4 h-4 m-0" />
56
66
  </button>
57
67
  )
58
68
  }
@@ -1,81 +1,99 @@
1
1
  import type React from "react"
2
+ import { useId } from "react"
3
+
4
+ const MOD = 1000
5
+ const SIZE = 64
6
+ const RADIUS = SIZE / 2
7
+ const prefix = "gradient-avatar-"
8
+
9
+ const scaledMod = (value: number, mod: number = MOD): number => {
10
+ return (value % mod) / (MOD / SIZE)
11
+ }
12
+
13
+ const cyrb53 = (str: string, seed: number = 0): number => {
14
+ let h1 = 0xdeadbeef ^ seed
15
+ let h2 = 0x41c6ce57 ^ seed
2
16
 
3
- // MetaMask-style hash function
4
- const hashCode = (str: string): number => {
5
- let hash = 0
6
17
  for (let i = 0; i < str.length; i++) {
7
- const char = str.charCodeAt(i)
8
- hash = (hash << 5) - hash + char
9
- hash = hash & hash
18
+ const ch = str.charCodeAt(i)
19
+ h1 = Math.imul(h1 ^ ch, 2654435761)
20
+ h2 = Math.imul(h2 ^ ch, 1597334677)
10
21
  }
11
- return Math.abs(hash)
22
+
23
+ h1 =
24
+ Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^
25
+ Math.imul(h2 ^ (h2 >>> 13), 3266489909)
26
+ h2 =
27
+ Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^
28
+ Math.imul(h1 ^ (h1 >>> 13), 3266489909)
29
+
30
+ return 4294967296 * (2097151 & h2) + (h1 >>> 0)
12
31
  }
13
32
 
14
- // Generate MetaMask-style colors (bright and vibrant)
15
- const generateMetaMaskColors = (seed: number) => {
16
- const colors = [
17
- "#FF6B6B",
18
- "#4ECDC4",
19
- "#45B7D1",
20
- "#96CEB4",
21
- "#FECA57",
22
- "#FF9FF3",
23
- "#54A0FF",
24
- "#5F27CD",
25
- "#00D2D3",
26
- "#FF9F43",
27
- "#10AC84",
28
- "#EE5A24",
29
- "#0ABDE3",
30
- "#C44569",
31
- "#F8B500",
32
- "#6C5CE7",
33
- "#A3CB38",
34
- "#FD79A8",
35
- "#636E72",
36
- "#00B894",
37
- ]
38
-
39
- const bg = colors[seed % colors.length]
40
- const spot = colors[(seed + 7) % colors.length]
41
-
42
- return { backgroundColor: bg, spotColor: spot }
33
+ const createId = (name: string, id: string): string => `${prefix}${name}${id}`
34
+
35
+ const createHues = (a: number, _b: number, c: number) => {
36
+ const hueA = a % 360
37
+ const hueB = (a + 120) % 360
38
+ const hueC = c % 360
39
+ return { hueA, hueB, hueC }
43
40
  }
44
41
 
45
- // Generate MetaMask-style pattern
46
- const generateMetaMaskPattern = (address: string) => {
47
- const cleanAddress = address.replace(/^0x/i, "").toLowerCase()
48
-
49
- // Use address to generate deterministic values
50
- const seed = hashCode(cleanAddress)
51
- const { backgroundColor, spotColor } = generateMetaMaskColors(seed)
52
-
53
- // Generate a 8x8 grid pattern like MetaMask
54
- const size = 8
55
- const pattern: boolean[][] = []
56
-
57
- // Generate pattern with symmetry (like MetaMask)
58
- for (let y = 0; y < size; y++) {
59
- const row: boolean[] = []
60
- for (let x = 0; x < size; x++) {
61
- // Create horizontal symmetry
62
- const actualX = x < 4 ? x : 7 - x
63
-
64
- // Use different parts of address for different rows/columns
65
- const addressIndex = (y * 4 + actualX) % cleanAddress.length
66
- const char = cleanAddress.charCodeAt(addressIndex)
67
-
68
- // Determine if this cell should be filled
69
- const shouldFill = (char + seed) % 3 === 0
70
- row.push(shouldFill)
71
- }
72
- pattern.push(row)
42
+ interface Gradient {
43
+ id: string
44
+ stopColor0: string
45
+ stopColor1: string
46
+ cx: number
47
+ cy: number
48
+ r: number
49
+ }
50
+
51
+ interface Gradients {
52
+ background: Gradient
53
+ primary: Gradient
54
+ secondary: Gradient
55
+ }
56
+
57
+ const createGradients = (id: string, address: string): Gradients => {
58
+ const hash = {
59
+ a: cyrb53(`${address}a`, 0),
60
+ b: cyrb53(`${address}b`, 1),
61
+ c: cyrb53(`${address}c`, 2),
62
+ x: cyrb53(`${address}d`, 3),
63
+ y: cyrb53(`${address}e`, 4),
64
+ r: cyrb53(`${address}f`, 5),
73
65
  }
74
66
 
67
+ const { hueA, hueB, hueC } = createHues(hash.a, hash.b, hash.c)
68
+ const cx = scaledMod(hash.x)
69
+ const cy = scaledMod(hash.y)
70
+ const r = SIZE / 10 + scaledMod(hash.r, MOD * 1.5)
71
+
75
72
  return {
76
- backgroundColor,
77
- spotColor,
78
- pattern,
73
+ background: {
74
+ id: createId("background", id),
75
+ stopColor0: `hsl(${hueC}deg 100% 50% / 1)`,
76
+ stopColor1: `hsl(${hueA}deg 100% 50% / 1)`,
77
+ cx,
78
+ cy,
79
+ r,
80
+ },
81
+ primary: {
82
+ id: createId("primary", id),
83
+ stopColor0: `hsl(${hueA}deg 100% 50% / 1)`,
84
+ stopColor1: `hsl(${hueB}deg 100% 50% / 1)`,
85
+ cx,
86
+ cy,
87
+ r,
88
+ },
89
+ secondary: {
90
+ id: createId("secondary", id),
91
+ stopColor0: `hsl(${hueC}deg 100% 50% / 1)`,
92
+ stopColor1: `hsl(${hueB}deg 100% 50% / 1)`,
93
+ cx: cy,
94
+ cy: cx,
95
+ r: r / 2,
96
+ },
79
97
  }
80
98
  }
81
99
 
@@ -90,6 +108,8 @@ export const Identicon: React.FC<IdenticonProps> = ({
90
108
  size = 64,
91
109
  className = "",
92
110
  }) => {
111
+ const id = useId()
112
+
93
113
  if (!value || typeof value !== "string") {
94
114
  return (
95
115
  <div
@@ -104,10 +124,8 @@ export const Identicon: React.FC<IdenticonProps> = ({
104
124
  )
105
125
  }
106
126
 
107
- const { backgroundColor, spotColor, pattern } = generateMetaMaskPattern(value)
108
-
109
- const gridSize = 8
110
- const cellSize = size / gridSize
127
+ const gradients = createGradients(id, value.toLowerCase())
128
+ const getId = (name: string) => createId(name, id)
111
129
 
112
130
  return (
113
131
  <div
@@ -117,39 +135,90 @@ export const Identicon: React.FC<IdenticonProps> = ({
117
135
  height: size,
118
136
  borderRadius: "50%",
119
137
  overflow: "hidden",
120
- backgroundColor: backgroundColor,
138
+ flexShrink: 0,
121
139
  }}
122
140
  >
123
141
  <svg
124
142
  width={size}
125
143
  height={size}
144
+ viewBox={`0 0 ${SIZE} ${SIZE}`}
126
145
  style={{ display: "block" }}
146
+ xmlns="http://www.w3.org/2000/svg"
127
147
  aria-label={`Identicon for ${value}`}
128
148
  >
129
149
  <title>Identicon for {value}</title>
130
- {/* Background circle */}
131
- <circle
132
- cx={size / 2}
133
- cy={size / 2}
134
- r={size / 2}
135
- fill={backgroundColor}
136
- />
137
-
138
- {/* Pattern cells */}
139
- {pattern.map((row, y) =>
140
- row.map((shouldFill, x) =>
141
- shouldFill ? (
142
- <rect
143
- key={`cell-${y}-${x}-${value.slice(-8)}`}
144
- x={x * cellSize}
145
- y={y * cellSize}
146
- width={cellSize}
147
- height={cellSize}
148
- fill={spotColor}
149
- />
150
- ) : null,
151
- ),
152
- )}
150
+
151
+ <defs>
152
+ <clipPath id={getId("circle-clip")}>
153
+ <circle cx={RADIUS} cy={RADIUS} r={RADIUS} />
154
+ </clipPath>
155
+
156
+ <filter
157
+ id={getId("blur-xs")}
158
+ x="-10%"
159
+ y="-10%"
160
+ width="120%"
161
+ height="120%"
162
+ filterUnits="userSpaceOnUse"
163
+ colorInterpolationFilters="sRGB"
164
+ >
165
+ <feFlood floodOpacity="0" result="BackgroundImageFix" />
166
+ <feBlend
167
+ mode="normal"
168
+ in="SourceGraphic"
169
+ in2="BackgroundImageFix"
170
+ result="shape"
171
+ />
172
+ <feGaussianBlur
173
+ stdDeviation={SIZE / 10}
174
+ result="effect1_foregroundBlur"
175
+ />
176
+ </filter>
177
+
178
+ <linearGradient
179
+ id={gradients.background.id}
180
+ x1="0"
181
+ y1="0"
182
+ x2="1"
183
+ y2="1"
184
+ >
185
+ <stop offset="0" stopColor={gradients.background.stopColor0} />
186
+ <stop offset="1" stopColor={gradients.background.stopColor1} />
187
+ </linearGradient>
188
+
189
+ <radialGradient id={gradients.primary.id}>
190
+ <stop offset="0" stopColor={gradients.primary.stopColor0} />
191
+ <stop offset="1" stopColor={gradients.primary.stopColor1} />
192
+ </radialGradient>
193
+
194
+ <radialGradient id={gradients.secondary.id}>
195
+ <stop offset="0" stopColor={gradients.secondary.stopColor0} />
196
+ <stop offset="1" stopColor={gradients.secondary.stopColor1} />
197
+ </radialGradient>
198
+ </defs>
199
+
200
+ <g clipPath={`url(#${getId("circle-clip")})`}>
201
+ <rect
202
+ width="100%"
203
+ height="100%"
204
+ fill={`url(#${gradients.background.id})`}
205
+ />
206
+
207
+ <g filter={`url(#${getId("blur-xs")})`}>
208
+ <circle
209
+ fill={`url(#${gradients.primary.id})`}
210
+ cx={gradients.primary.cx}
211
+ cy={gradients.primary.cy}
212
+ r={gradients.primary.r}
213
+ />
214
+ <circle
215
+ fill={`url(#${gradients.secondary.id})`}
216
+ cx={gradients.secondary.cx}
217
+ cy={gradients.secondary.cy}
218
+ r={gradients.secondary.r}
219
+ />
220
+ </g>
221
+ </g>
153
222
  </svg>
154
223
  </div>
155
224
  )
@@ -12,24 +12,18 @@ import CoinbaseLogo from "../assets/Coinbase_Icon_Logo.svg"
12
12
  import { ScreenHeader } from "./ScreenHeader.js"
13
13
  import { logger } from "../../logger.js"
14
14
  import { useSelectedMeshExchange } from "../hooks/useSelectedMeshExchange.js"
15
- import { useMode } from "../hooks/useMode.js"
16
15
  import { useCurrentScreen } from "../hooks/useCurrentScreen.js"
17
- import type { Mode } from "../../mode.js"
18
- import type { Screen } from "../hooks/useCurrentScreen.js"
19
16
 
20
17
  export interface MeshConnectExchangesProps {
21
18
  onBack?: () => void
22
19
  onSelectExchange?: (integrationId: string, exchangeName: string) => void
23
- getInitialScreenForMode?: (mode: Mode) => Screen
24
20
  }
25
21
 
26
22
  export const MeshConnectExchanges: React.FC<MeshConnectExchangesProps> = ({
27
23
  onBack,
28
24
  onSelectExchange,
29
- getInitialScreenForMode,
30
25
  }) => {
31
26
  const { setSelectedExchange } = useSelectedMeshExchange()
32
- const { mode } = useMode()
33
27
  const { setCurrentScreen } = useCurrentScreen()
34
28
  const [coinbaseId, setCoinbaseId] = useState<string | null>(null)
35
29
  const [binanceId, setBinanceId] = useState<string | null>(null)
@@ -94,15 +88,8 @@ export const MeshConnectExchanges: React.FC<MeshConnectExchangesProps> = ({
94
88
  // Store the selected exchange
95
89
  setSelectedExchange({ integrationId, exchangeName })
96
90
 
97
- // Navigate to the initial screen for the current mode
98
- if (getInitialScreenForMode) {
99
- const currentMode = (mode || "pay") as Mode
100
- const initialScreen = getInitialScreenForMode(currentMode)
101
- logger.console.log(
102
- `[trails-sdk] Navigating to initial screen for mode ${currentMode}: ${initialScreen}`,
103
- )
104
- setCurrentScreen(initialScreen)
105
- }
91
+ // Navigate to home screen
92
+ setCurrentScreen("home")
106
93
  }
107
94
 
108
95
  if (loading) {