0xtrails 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/dist/analytics.d.ts +8 -3
  2. package/dist/analytics.d.ts.map +1 -1
  3. package/dist/{ccip-DhEkQ6QC.js → ccip-Dw5AN7oU.js} +1 -1
  4. package/dist/cctp.d.ts +0 -149
  5. package/dist/cctp.d.ts.map +1 -1
  6. package/dist/chains.d.ts +28 -3
  7. package/dist/chains.d.ts.map +1 -1
  8. package/dist/config.d.ts +11 -0
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/constants.d.ts +1 -1
  11. package/dist/constants.d.ts.map +1 -1
  12. package/dist/contractUtils.d.ts.map +1 -1
  13. package/dist/estimate.d.ts.map +1 -1
  14. package/dist/fees.d.ts.map +1 -1
  15. package/dist/gasless.d.ts +12 -0
  16. package/dist/gasless.d.ts.map +1 -1
  17. package/dist/{index-MhD2DA7_.js → index-BtVUTbEZ.js} +30984 -38945
  18. package/dist/index.d.ts +7 -5
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +108 -107
  21. package/dist/indexerClient.d.ts +2 -2
  22. package/dist/intents.d.ts +0 -17
  23. package/dist/intents.d.ts.map +1 -1
  24. package/dist/mutations.d.ts.map +1 -1
  25. package/dist/paymasterSend.d.ts.map +1 -1
  26. package/dist/prepareSend.d.ts +1 -1
  27. package/dist/prepareSend.d.ts.map +1 -1
  28. package/dist/sendUserOp.d.ts +0 -18
  29. package/dist/sendUserOp.d.ts.map +1 -1
  30. package/dist/tokenBalances.d.ts.map +1 -1
  31. package/dist/tokens.d.ts +10 -8
  32. package/dist/tokens.d.ts.map +1 -1
  33. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts +4 -5
  34. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
  35. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts +4 -5
  36. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
  37. package/dist/transactionIntent/deposits/standardDeposit.d.ts +2 -2
  38. package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
  39. package/dist/transactionIntent/execution/transactionState.d.ts +2 -2
  40. package/dist/transactionIntent/execution/transactionState.d.ts.map +1 -1
  41. package/dist/transactionIntent/handlers/crossChain.d.ts +4 -4
  42. package/dist/transactionIntent/handlers/crossChain.d.ts.map +1 -1
  43. package/dist/transactionIntent/handlers/index.d.ts +0 -1
  44. package/dist/transactionIntent/handlers/index.d.ts.map +1 -1
  45. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts +4 -34
  46. package/dist/transactionIntent/handlers/sameChainSameToken.d.ts.map +1 -1
  47. package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
  48. package/dist/transactionIntent/quote/quoteHelpers.d.ts +2 -1
  49. package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
  50. package/dist/transactionIntent/types.d.ts +6 -19
  51. package/dist/transactionIntent/types.d.ts.map +1 -1
  52. package/dist/transactionIntent/utils/index.d.ts +0 -1
  53. package/dist/transactionIntent/utils/index.d.ts.map +1 -1
  54. package/dist/transactions.d.ts +2 -20
  55. package/dist/transactions.d.ts.map +1 -1
  56. package/dist/utils.d.ts +8 -2
  57. package/dist/utils.d.ts.map +1 -1
  58. package/dist/walletUtils.d.ts +21 -0
  59. package/dist/walletUtils.d.ts.map +1 -0
  60. package/dist/wallets.d.ts +33 -240
  61. package/dist/wallets.d.ts.map +1 -1
  62. package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
  63. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  64. package/dist/widget/components/FeeOption.d.ts +8 -13
  65. package/dist/widget/components/FeeOption.d.ts.map +1 -1
  66. package/dist/widget/components/FeeOptions.d.ts +11 -5
  67. package/dist/widget/components/FeeOptions.d.ts.map +1 -1
  68. package/dist/widget/components/NativeGasOption.d.ts.map +1 -1
  69. package/dist/widget/components/Pay.d.ts.map +1 -1
  70. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  71. package/dist/widget/components/QRCodeDeposit.d.ts +5 -0
  72. package/dist/widget/components/QRCodeDeposit.d.ts.map +1 -1
  73. package/dist/widget/components/QRCodeWalletSelect.d.ts +13 -0
  74. package/dist/widget/components/QRCodeWalletSelect.d.ts.map +1 -0
  75. package/dist/widget/components/QrCode.d.ts.map +1 -1
  76. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  77. package/dist/widget/components/Receipt.d.ts.map +1 -1
  78. package/dist/widget/components/ScreenHeader.d.ts +1 -1
  79. package/dist/widget/components/ScreenHeader.d.ts.map +1 -1
  80. package/dist/widget/components/Toast.d.ts.map +1 -1
  81. package/dist/widget/components/TokenImage.d.ts.map +1 -1
  82. package/dist/widget/css/compiled.css +1 -1
  83. package/dist/widget/hooks/useCheckout.d.ts +15 -1
  84. package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
  85. package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
  86. package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
  87. package/dist/widget/hooks/useDebugScreens.d.ts +1 -1
  88. package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -1
  89. package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
  90. package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts +7 -0
  91. package/dist/widget/hooks/useIsConnectedWalletSmartContract.d.ts.map +1 -0
  92. package/dist/widget/hooks/useIsSequenceWallet.d.ts +6 -0
  93. package/dist/widget/hooks/useIsSequenceWallet.d.ts.map +1 -0
  94. package/dist/widget/hooks/useQuote.d.ts +5 -8
  95. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  96. package/dist/widget/hooks/useRecentTokens.d.ts.map +1 -1
  97. package/dist/widget/hooks/useSelectedFeeOption.d.ts +30 -0
  98. package/dist/widget/hooks/useSelectedFeeOption.d.ts.map +1 -0
  99. package/dist/widget/hooks/useSendForm.d.ts +6 -15
  100. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  101. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  102. package/dist/widget/index.js +1 -1
  103. package/dist/widget/providers/TrailsProvider.d.ts +23 -12
  104. package/dist/widget/providers/TrailsProvider.d.ts.map +1 -1
  105. package/dist/widget/widget.d.ts +11 -0
  106. package/dist/widget/widget.d.ts.map +1 -1
  107. package/package.json +8 -8
  108. package/src/analytics.ts +53 -21
  109. package/src/cctp.ts +0 -1016
  110. package/src/chains.ts +93 -39
  111. package/src/config.ts +24 -6
  112. package/src/constants.ts +1 -4
  113. package/src/contractUtils.ts +6 -6
  114. package/src/estimate.ts +3 -6
  115. package/src/fees.ts +5 -10
  116. package/src/gasless.ts +45 -0
  117. package/src/index.ts +7 -6
  118. package/src/indexerClient.ts +2 -2
  119. package/src/intents.ts +52 -206
  120. package/src/mutations.ts +3 -2
  121. package/src/paymasterSend.ts +2 -5
  122. package/src/prepareSend.ts +9 -12
  123. package/src/sendUserOp.ts +3 -64
  124. package/src/tokenBalances.ts +2 -1
  125. package/src/tokens.ts +62 -133
  126. package/src/trailsClient.ts +1 -1
  127. package/src/transactionIntent/deposits/depositOrchestrator.ts +14 -15
  128. package/src/transactionIntent/deposits/gaslessDeposit.ts +70 -100
  129. package/src/transactionIntent/deposits/standardDeposit.ts +22 -28
  130. package/src/transactionIntent/execution/transactionState.ts +2 -2
  131. package/src/transactionIntent/handlers/crossChain.ts +165 -385
  132. package/src/transactionIntent/handlers/index.ts +0 -1
  133. package/src/transactionIntent/handlers/sameChainSameToken.ts +228 -94
  134. package/src/transactionIntent/quote/normalizeQuote.ts +4 -6
  135. package/src/transactionIntent/quote/quoteHelpers.ts +35 -3
  136. package/src/transactionIntent/types.ts +6 -27
  137. package/src/transactionIntent/utils/index.ts +0 -1
  138. package/src/transactions.ts +6 -203
  139. package/src/umd.tsx +1 -3
  140. package/src/utils.ts +28 -8
  141. package/src/walletUtils.ts +42 -0
  142. package/src/wallets.ts +361 -203
  143. package/src/widget/compiled.css +1 -1
  144. package/src/widget/components/AccountIntentTransactionHistory.tsx +73 -4
  145. package/src/widget/components/AccountSettings.tsx +17 -17
  146. package/src/widget/components/ChainList.tsx +3 -3
  147. package/src/widget/components/ClassicSwap.tsx +19 -10
  148. package/src/widget/components/ConfigDisplay.tsx +1 -1
  149. package/src/widget/components/FeeOption.tsx +63 -20
  150. package/src/widget/components/FeeOptions.tsx +54 -123
  151. package/src/widget/components/NativeGasOption.tsx +3 -1
  152. package/src/widget/components/Pay.tsx +18 -11
  153. package/src/widget/components/PoolDeposit.tsx +23 -10
  154. package/src/widget/components/QRCodeDeposit.tsx +50 -30
  155. package/src/widget/components/QRCodeWalletSelect.tsx +77 -0
  156. package/src/widget/components/QrCode.tsx +188 -233
  157. package/src/widget/components/QuoteDetails.tsx +48 -2
  158. package/src/widget/components/Receipt.tsx +5 -2
  159. package/src/widget/components/ScreenHeader.tsx +10 -8
  160. package/src/widget/components/Toast.tsx +10 -0
  161. package/src/widget/components/TokenImage.tsx +56 -13
  162. package/src/widget/hooks/useCheckout.ts +71 -0
  163. package/src/widget/hooks/useCurrentScreen.tsx +1 -0
  164. package/src/widget/hooks/useDebugScreens.ts +5 -0
  165. package/src/widget/hooks/useIntentTransactionHistory.ts +788 -418
  166. package/src/widget/hooks/useIsConnectedWalletSmartContract.ts +43 -0
  167. package/src/widget/hooks/useIsSequenceWallet.ts +17 -0
  168. package/src/widget/hooks/useQuote.ts +16 -17
  169. package/src/widget/hooks/useRecentTokens.ts +2 -1
  170. package/src/widget/hooks/useSelectedFeeOption.tsx +257 -0
  171. package/src/widget/hooks/useSendForm.ts +172 -47
  172. package/src/widget/hooks/useTokenList.ts +15 -2
  173. package/src/widget/providers/TrailsProvider.tsx +53 -25
  174. package/src/widget/widget.tsx +119 -48
  175. package/dist/cctpqueue.d.ts +0 -18
  176. package/dist/cctpqueue.d.ts.map +0 -1
  177. package/dist/preconditions.d.ts +0 -12
  178. package/dist/preconditions.d.ts.map +0 -1
  179. package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts +0 -62
  180. package/dist/transactionIntent/handlers/sameChainDifferentToken.d.ts.map +0 -1
  181. package/dist/transactionIntent/utils/lifiHelpers.d.ts +0 -10
  182. package/dist/transactionIntent/utils/lifiHelpers.d.ts.map +0 -1
  183. package/dist/widget/hooks/useSelectedFeeToken.d.ts +0 -33
  184. package/dist/widget/hooks/useSelectedFeeToken.d.ts.map +0 -1
  185. package/src/cctpqueue.ts +0 -69
  186. package/src/preconditions.ts +0 -47
  187. package/src/transactionIntent/handlers/sameChainDifferentToken.ts +0 -323
  188. package/src/transactionIntent/utils/lifiHelpers.ts +0 -68
  189. package/src/widget/hooks/useSelectedFeeToken.tsx +0 -288
@@ -9,36 +9,26 @@ interface QrCodeProps {
9
9
  className?: string
10
10
  }
11
11
 
12
- const CONNECTING_ERROR_MARGIN = 0.1
13
- const CIRCLE_SIZE_MODIFIER = 2.5
14
- const QRCODE_MATRIX_MARGIN = 7
15
-
16
- function isAdjecentDots(cy: number, otherCy: number, cellSize: number) {
17
- if (cy === otherCy) {
18
- return false
19
- }
20
- const diff = cy - otherCy < 0 ? otherCy - cy : cy - otherCy
21
- return diff <= cellSize + CONNECTING_ERROR_MARGIN
22
- }
23
-
24
12
  function getMatrix(
25
13
  value: string,
26
14
  errorCorrectionLevel: QRCodeUtil.QRCodeErrorCorrectionLevel,
27
15
  ) {
28
- const arr = Array.prototype.slice.call(
29
- QRCodeUtil.create(value, {
30
- errorCorrectionLevel,
31
- }).modules.data,
32
- 0,
33
- )
34
- const sqrt = Math.sqrt(arr.length)
35
- return arr.reduce(
36
- (rows, key, index) =>
37
- (index % sqrt === 0
38
- ? rows.push([key])
39
- : rows[rows.length - 1].push(key)) && rows,
40
- [],
41
- )
16
+ const qr = QRCodeUtil.create(value, {
17
+ errorCorrectionLevel,
18
+ })
19
+
20
+ const { data, size } = qr.modules
21
+ const matrix: number[][] = []
22
+
23
+ for (let row = 0; row < size; row += 1) {
24
+ const rowData: number[] = []
25
+ for (let col = 0; col < size; col += 1) {
26
+ rowData.push(data[row * size + col] ? 1 : 0)
27
+ }
28
+ matrix.push(rowData)
29
+ }
30
+
31
+ return matrix
42
32
  }
43
33
 
44
34
  export const QrCode: React.FC<QrCodeProps> = ({
@@ -50,225 +40,191 @@ export const QrCode: React.FC<QrCodeProps> = ({
50
40
  const svgRef = useRef<SVGSVGElement>(null)
51
41
 
52
42
  useEffect(() => {
53
- if (!svgRef.current) return
43
+ const svg = svgRef.current
44
+ if (!svg) return
54
45
 
55
46
  // Clear existing content
56
- while (svgRef.current.firstChild) {
57
- svgRef.current.removeChild(svgRef.current.firstChild)
47
+ while (svg.firstChild) {
48
+ svg.removeChild(svg.firstChild)
58
49
  }
59
50
 
60
- const strokeWidth = 5
61
- const padding = 8
62
- const logoSize = size * 0.16 // 16% of size for logo
63
- const matrix = getMatrix(url, "Q")
64
- const cellSize = (size - 2 * padding) / matrix.length
65
-
66
- // Corner squares positions
67
- const qrList = [
68
- { x: 0, y: 0 }, // Top-left
69
- { x: 1, y: 0 }, // Top-right
70
- { x: 0, y: 1 }, // Bottom-left
71
- ]
72
-
73
- // Draw corner squares
74
- qrList.forEach(({ x, y }) => {
75
- const x1 = (matrix.length - QRCODE_MATRIX_MARGIN) * cellSize * x + padding
76
- const y1 = (matrix.length - QRCODE_MATRIX_MARGIN) * cellSize * y + padding
77
- const borderRadius = 0.45
78
-
79
- for (let i = 0; i < qrList.length; i += 1) {
80
- const dotSize = cellSize * (QRCODE_MATRIX_MARGIN - i * 2)
81
- const rect = document.createElementNS(
82
- "http://www.w3.org/2000/svg",
83
- "rect",
84
- )
51
+ if (!url) return
85
52
 
86
- if (i === 0) {
87
- // Outer square: transparent with dark stroke
88
- rect.setAttribute("fill", "transparent")
89
- rect.setAttribute("stroke", "#141414")
90
- rect.setAttribute("stroke-width", strokeWidth.toString())
91
- rect.setAttribute("width", (dotSize - strokeWidth).toString())
92
- rect.setAttribute("height", (dotSize - strokeWidth).toString())
93
- rect.setAttribute(
94
- "rx",
95
- ((dotSize - strokeWidth) * borderRadius).toString(),
96
- )
97
- rect.setAttribute(
98
- "ry",
99
- ((dotSize - strokeWidth) * borderRadius).toString(),
100
- )
101
- rect.setAttribute(
102
- "x",
103
- (y1 + cellSize * i + strokeWidth / 2).toString(),
104
- )
105
- rect.setAttribute(
106
- "y",
107
- (x1 + cellSize * i + strokeWidth / 2).toString(),
108
- )
109
- } else if (i === 1) {
110
- // Middle square: transparent with no stroke
111
- rect.setAttribute("fill", "transparent")
112
- rect.setAttribute("stroke", "#141414")
113
- rect.setAttribute("stroke-width", "0")
114
- rect.setAttribute("width", dotSize.toString())
115
- rect.setAttribute("height", dotSize.toString())
116
- rect.setAttribute("rx", (dotSize * borderRadius).toString())
117
- rect.setAttribute("ry", (dotSize * borderRadius).toString())
118
- rect.setAttribute("x", (y1 + cellSize * i).toString())
119
- rect.setAttribute("y", (x1 + cellSize * i).toString())
120
- } else {
121
- // Inner square: dark fill with no stroke
122
- rect.setAttribute("fill", "#141414")
123
- rect.setAttribute("stroke", "#141414")
124
- rect.setAttribute("stroke-width", "0")
125
- rect.setAttribute("width", dotSize.toString())
126
- rect.setAttribute("height", dotSize.toString())
127
- rect.setAttribute("rx", (dotSize * borderRadius).toString())
128
- rect.setAttribute("ry", (dotSize * borderRadius).toString())
129
- rect.setAttribute("x", (y1 + cellSize * i).toString())
130
- rect.setAttribute("y", (x1 + cellSize * i).toString())
131
- }
132
-
133
- if (svgRef.current) {
134
- svgRef.current.appendChild(rect)
135
- }
136
- }
137
- })
138
-
139
- // Calculate logo area to exclude from data dots
140
- const clearArenaSize = Math.floor((logoSize + 25) / cellSize)
141
- const matrixMiddleStart = matrix.length / 2 - clearArenaSize / 2
142
- const matrixMiddleEnd = matrix.length / 2 + clearArenaSize / 2 - 1
143
-
144
- const circles: [number, number][] = []
145
-
146
- // Get coordinates for each QR code dot
147
- matrix.forEach((row: QRCodeUtil.QRCode[], i: number) => {
148
- row.forEach((_, j: number) => {
149
- if (matrix[i][j]) {
150
- // Skip corner areas
151
- if (
152
- !(
153
- (i < QRCODE_MATRIX_MARGIN && j < QRCODE_MATRIX_MARGIN) ||
154
- (i > matrix.length - (QRCODE_MATRIX_MARGIN + 1) &&
155
- j < QRCODE_MATRIX_MARGIN) ||
156
- (i < QRCODE_MATRIX_MARGIN &&
157
- j > matrix.length - (QRCODE_MATRIX_MARGIN + 1))
53
+ const matrix = getMatrix(url, "Q")
54
+ const matrixSize = matrix.length
55
+ if (!matrixSize) return
56
+
57
+ const quietZone = 16
58
+ const qrCodeSize = size - quietZone * 2
59
+ const padding = 12
60
+ const fillColor = "#000000"
61
+ const backgroundColor = "#ffffff"
62
+ const quietZoneColor = "#ffffff"
63
+ const cellSize = (qrCodeSize - padding * 2) / matrixSize
64
+ const shouldReserveLogoSpace = Boolean(imageUrl)
65
+ const logoSize = shouldReserveLogoSpace ? qrCodeSize * 0.18 : 0
66
+ const logoMatrixSize = shouldReserveLogoSpace
67
+ ? Math.max(2, Math.floor(logoSize / cellSize))
68
+ : 0
69
+ const matrixMiddleStart = shouldReserveLogoSpace
70
+ ? Math.floor((matrixSize - logoMatrixSize) / 2)
71
+ : 0
72
+ const matrixMiddleEnd = shouldReserveLogoSpace
73
+ ? matrixMiddleStart + logoMatrixSize - 1
74
+ : -1
75
+
76
+ svg.setAttribute("shape-rendering", "geometricPrecision")
77
+
78
+ // Outer container background (light grey)
79
+ const outerBackground = document.createElementNS(
80
+ "http://www.w3.org/2000/svg",
81
+ "rect",
82
+ )
83
+ outerBackground.setAttribute("x", "0")
84
+ outerBackground.setAttribute("y", "0")
85
+ outerBackground.setAttribute("width", size.toString())
86
+ outerBackground.setAttribute("height", size.toString())
87
+ outerBackground.setAttribute("fill", "#f3f4f6")
88
+ outerBackground.setAttribute("rx", "12")
89
+ outerBackground.setAttribute("ry", "12")
90
+ svg.appendChild(outerBackground)
91
+
92
+ // White quiet zone border
93
+ const quietZoneRect = document.createElementNS(
94
+ "http://www.w3.org/2000/svg",
95
+ "rect",
96
+ )
97
+ quietZoneRect.setAttribute("x", quietZone.toString())
98
+ quietZoneRect.setAttribute("y", quietZone.toString())
99
+ quietZoneRect.setAttribute("width", qrCodeSize.toString())
100
+ quietZoneRect.setAttribute("height", qrCodeSize.toString())
101
+ quietZoneRect.setAttribute("fill", quietZoneColor)
102
+ quietZoneRect.setAttribute("rx", "8")
103
+ quietZoneRect.setAttribute("ry", "8")
104
+ svg.appendChild(quietZoneRect)
105
+
106
+ // QR code white background
107
+ const qrBackground = document.createElementNS(
108
+ "http://www.w3.org/2000/svg",
109
+ "rect",
110
+ )
111
+ qrBackground.setAttribute("x", (quietZone + padding).toString())
112
+ qrBackground.setAttribute("y", (quietZone + padding).toString())
113
+ qrBackground.setAttribute("width", (qrCodeSize - padding * 2).toString())
114
+ qrBackground.setAttribute("height", (qrCodeSize - padding * 2).toString())
115
+ qrBackground.setAttribute("fill", backgroundColor)
116
+ qrBackground.setAttribute("rx", "6")
117
+ qrBackground.setAttribute("ry", "6")
118
+ svg.appendChild(qrBackground)
119
+
120
+ // Draw continuous lines by grouping adjacent modules
121
+ matrix.forEach((row, rowIndex) => {
122
+ const isInsideLogoRow =
123
+ shouldReserveLogoSpace &&
124
+ rowIndex >= matrixMiddleStart &&
125
+ rowIndex <= matrixMiddleEnd
126
+
127
+ let currentRunStart: number | null = null
128
+
129
+ row.forEach((value, colIndex) => {
130
+ const isInsideLogoArea =
131
+ isInsideLogoRow &&
132
+ colIndex >= matrixMiddleStart &&
133
+ colIndex <= matrixMiddleEnd
134
+
135
+ if (isInsideLogoArea) {
136
+ // End current run if we hit logo area
137
+ if (currentRunStart !== null) {
138
+ const runLength = colIndex - currentRunStart
139
+ const rect = document.createElementNS(
140
+ "http://www.w3.org/2000/svg",
141
+ "rect",
142
+ )
143
+ rect.setAttribute(
144
+ "x",
145
+ (quietZone + padding + currentRunStart * cellSize).toFixed(3),
158
146
  )
159
- ) {
160
- // Skip logo area
161
- if (
162
- !(
163
- i > matrixMiddleStart &&
164
- i < matrixMiddleEnd &&
165
- j > matrixMiddleStart &&
166
- j < matrixMiddleEnd
167
- )
168
- ) {
169
- const cx = i * cellSize + cellSize / 2 + padding
170
- const cy = j * cellSize + cellSize / 2 + padding
171
- circles.push([cx, cy])
172
- }
147
+ rect.setAttribute(
148
+ "y",
149
+ (quietZone + padding + rowIndex * cellSize).toFixed(3),
150
+ )
151
+ rect.setAttribute("width", (runLength * cellSize).toFixed(3))
152
+ rect.setAttribute("height", cellSize.toFixed(3))
153
+ rect.setAttribute("fill", fillColor)
154
+ svg.appendChild(rect)
155
+ currentRunStart = null
173
156
  }
157
+ return
174
158
  }
175
- })
176
- })
177
-
178
- // Group dots by x-coordinate
179
- const circlesToConnect: Record<number, number[]> = {}
180
- circles.forEach(([cx, cy]) => {
181
- if (circlesToConnect[cx]) {
182
- circlesToConnect[cx]?.push(cy)
183
- } else {
184
- circlesToConnect[cx] = [cy]
185
- }
186
- })
187
159
 
188
- // Draw individual dots (those without neighbors)
189
- Object.entries(circlesToConnect)
190
- .map(([cx, cys]) => {
191
- const newCys = cys.filter((cy) =>
192
- cys.every((otherCy) => !isAdjecentDots(cy, otherCy, cellSize)),
193
- )
194
- return [Number(cx), newCys] as [number, number[]]
195
- })
196
- .forEach(([cx, cys]) => {
197
- cys.forEach((cy) => {
198
- const circle = document.createElementNS(
199
- "http://www.w3.org/2000/svg",
200
- "circle",
201
- )
202
- circle.setAttribute("cx", cx.toString())
203
- circle.setAttribute("cy", cy.toString())
204
- circle.setAttribute("fill", "#141414")
205
- circle.setAttribute("r", (cellSize / CIRCLE_SIZE_MODIFIER).toString())
206
- if (svgRef.current) {
207
- svgRef.current.appendChild(circle)
160
+ if (value) {
161
+ // Start a new run or continue existing one
162
+ if (currentRunStart === null) {
163
+ currentRunStart = colIndex
208
164
  }
209
- })
210
- })
211
-
212
- // Draw dots for connected groups (instead of lines)
213
- Object.entries(circlesToConnect)
214
- .filter(([_, cys]) => cys.length > 1)
215
- .map(([cx, cys]) => {
216
- const newCys = cys.filter((cy) =>
217
- cys.some((otherCy) => isAdjecentDots(cy, otherCy, cellSize)),
218
- )
219
- return [Number(cx), newCys] as [number, number[]]
220
- })
221
- .map(([cx, cys]) => {
222
- cys.sort((a, b) => (a < b ? -1 : 1))
223
- const groups: number[][] = []
224
- for (const cy of cys) {
225
- const group = groups.find((item) =>
226
- item.some((otherCy) => isAdjecentDots(cy, otherCy, cellSize)),
227
- )
228
- if (group) {
229
- group.push(cy)
230
- } else {
231
- groups.push([cy])
165
+ } else {
166
+ // End current run
167
+ if (currentRunStart !== null) {
168
+ const runLength = colIndex - currentRunStart
169
+ const rect = document.createElementNS(
170
+ "http://www.w3.org/2000/svg",
171
+ "rect",
172
+ )
173
+ rect.setAttribute(
174
+ "x",
175
+ (quietZone + padding + currentRunStart * cellSize).toFixed(3),
176
+ )
177
+ rect.setAttribute(
178
+ "y",
179
+ (quietZone + padding + rowIndex * cellSize).toFixed(3),
180
+ )
181
+ rect.setAttribute("width", (runLength * cellSize).toFixed(3))
182
+ rect.setAttribute("height", cellSize.toFixed(3))
183
+ rect.setAttribute("fill", fillColor)
184
+ svg.appendChild(rect)
185
+ currentRunStart = null
232
186
  }
233
187
  }
234
- return [cx, groups.map((item) => [item[0], item[item.length - 1]])] as [
235
- number,
236
- number[][],
237
- ]
238
188
  })
239
- .forEach(([cx, groups]) => {
240
- groups.forEach(([y1, y2]) => {
241
- if (y1 !== undefined && y2 !== undefined) {
242
- // Instead of drawing a line, draw individual dots at each position
243
- const positions = []
244
- for (let y = y1; y <= y2; y += cellSize) {
245
- positions.push(y)
246
- }
247
189
 
248
- positions.forEach((y) => {
249
- const circle = document.createElementNS(
250
- "http://www.w3.org/2000/svg",
251
- "circle",
252
- )
253
- circle.setAttribute("cx", cx.toString())
254
- circle.setAttribute("cy", y.toString())
255
- circle.setAttribute("fill", "#141414")
256
- circle.setAttribute(
257
- "r",
258
- (cellSize / CIRCLE_SIZE_MODIFIER).toString(),
259
- )
260
- if (svgRef.current) {
261
- svgRef.current.appendChild(circle)
262
- }
263
- })
264
- }
265
- })
266
- })
190
+ // End any remaining run at the end of the row
191
+ if (currentRunStart !== null) {
192
+ const runLength = matrixSize - currentRunStart
193
+ const rect = document.createElementNS(
194
+ "http://www.w3.org/2000/svg",
195
+ "rect",
196
+ )
197
+ rect.setAttribute(
198
+ "x",
199
+ (quietZone + padding + currentRunStart * cellSize).toFixed(3),
200
+ )
201
+ rect.setAttribute(
202
+ "y",
203
+ (quietZone + padding + rowIndex * cellSize).toFixed(3),
204
+ )
205
+ rect.setAttribute("width", (runLength * cellSize).toFixed(3))
206
+ rect.setAttribute("height", cellSize.toFixed(3))
207
+ rect.setAttribute("fill", fillColor)
208
+ svg.appendChild(rect)
209
+ }
210
+ })
267
211
 
268
- // Add logo if provided
269
212
  if (imageUrl) {
270
- const logoX = (size - logoSize) / 2
271
- const logoY = (size - logoSize) / 2
213
+ const logoX = quietZone + (qrCodeSize - logoSize) / 2
214
+ const logoY = quietZone + (qrCodeSize - logoSize) / 2
215
+
216
+ const logoBackground = document.createElementNS(
217
+ "http://www.w3.org/2000/svg",
218
+ "rect",
219
+ )
220
+ logoBackground.setAttribute("x", logoX.toString())
221
+ logoBackground.setAttribute("y", logoY.toString())
222
+ logoBackground.setAttribute("width", logoSize.toString())
223
+ logoBackground.setAttribute("height", logoSize.toString())
224
+ logoBackground.setAttribute("fill", backgroundColor)
225
+ logoBackground.setAttribute("rx", (logoSize * 0.2).toString())
226
+ logoBackground.setAttribute("ry", (logoSize * 0.2).toString())
227
+ svg.appendChild(logoBackground)
272
228
 
273
229
  const logo = document.createElementNS(
274
230
  "http://www.w3.org/2000/svg",
@@ -279,9 +235,8 @@ export const QrCode: React.FC<QrCodeProps> = ({
279
235
  logo.setAttribute("y", logoY.toString())
280
236
  logo.setAttribute("width", logoSize.toString())
281
237
  logo.setAttribute("height", logoSize.toString())
282
- if (svgRef.current) {
283
- svgRef.current.appendChild(logo)
284
- }
238
+ logo.setAttribute("preserveAspectRatio", "xMidYMid meet")
239
+ svg.appendChild(logo)
285
240
  }
286
241
  }, [url, imageUrl, size])
287
242
 
@@ -292,7 +247,7 @@ export const QrCode: React.FC<QrCodeProps> = ({
292
247
  width={size}
293
248
  height={size}
294
249
  viewBox={`0 0 ${size} ${size}`}
295
- className="rounded-lg bg-white"
250
+ className="rounded-xl"
296
251
  />
297
252
  </div>
298
253
  )
@@ -5,11 +5,12 @@ import { Tooltip } from "./Tooltip.js"
5
5
  import type React from "react"
6
6
  import { getExplorerUrlForAddress } from "../../explorer.js"
7
7
  import type { PrepareSendQuote } from "../../prepareSend.js"
8
- import { useState, useEffect, useRef } from "react"
8
+ import { useState, useEffect, useRef, useCallback } from "react"
9
9
  import { truncateAddress } from "../../utils.js"
10
10
  import { PriceImpactWarning } from "./PriceImpactWarning.js"
11
11
  import { usePriceImpactWarning } from "../hooks/usePriceImpactWarning.js"
12
12
  import { formatUsdAmountDisplay } from "../../tokenBalances.js"
13
+ import { Copy, Check } from "lucide-react"
13
14
 
14
15
  interface QuoteDetailsProps {
15
16
  quote?: PrepareSendQuote | null
@@ -56,6 +57,7 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
56
57
  const [showOriginRate, setShowOriginRate] = useState(true)
57
58
  const [isExpanded, setIsExpanded] = useState(false)
58
59
  const [showMoreInfo, setShowMoreInfo] = useState(false)
60
+ const [intentIdCopied, setIntentIdCopied] = useState(false)
59
61
  const containerRef = useRef<HTMLDivElement>(null)
60
62
  const calldataRef = useRef<HTMLDivElement>(null)
61
63
  const moreInfoRef = useRef<HTMLDivElement>(null)
@@ -78,6 +80,22 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
78
80
  onExpand?.(isExpanded)
79
81
  }, [isExpanded, onExpand])
80
82
 
83
+ const handleCopyIntentId = useCallback(async () => {
84
+ if (!quote?.intentId) return
85
+ try {
86
+ await navigator.clipboard.writeText(quote.intentId)
87
+ setIntentIdCopied(true)
88
+ } catch (error) {
89
+ console.error("[trails-sdk] Failed to copy intentId", error)
90
+ }
91
+ }, [quote?.intentId])
92
+
93
+ useEffect(() => {
94
+ if (!intentIdCopied) return
95
+ const timeout = setTimeout(() => setIntentIdCopied(false), 1500)
96
+ return () => clearTimeout(timeout)
97
+ }, [intentIdCopied])
98
+
81
99
  if (!showContent) return null
82
100
 
83
101
  return (
@@ -729,7 +747,9 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
729
747
  Number(quote.slippageTolerance) !== 0
730
748
  const hasPriceImpact =
731
749
  quote?.priceImpact != null && Number(quote.priceImpact) !== 0
732
- const showSection = hasSlippage || hasPriceImpact
750
+ const hasIntentId =
751
+ quote?.intentId != null && quote.intentId !== ""
752
+ const showSection = hasSlippage || hasPriceImpact || hasIntentId
733
753
 
734
754
  if (!showSection) return null
735
755
 
@@ -739,6 +759,32 @@ export const QuoteDetails: React.FC<QuoteDetailsProps> = ({
739
759
  Transaction details
740
760
  </div>
741
761
 
762
+ {/* Intent ID */}
763
+ {hasIntentId && (
764
+ <div className="flex justify-between items-center gap-2">
765
+ <span className="text-xs text-gray-600 dark:text-gray-400">
766
+ Intent ID:
767
+ </span>
768
+ <div className="flex items-center gap-2 text-xs font-medium text-gray-900 dark:text-white">
769
+ <span title={quote.intentId ?? undefined}>
770
+ {truncateAddress(quote?.intentId ?? "", 10, 8)}
771
+ </span>
772
+ <button
773
+ type="button"
774
+ onClick={handleCopyIntentId}
775
+ className="p-1 rounded-full hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
776
+ aria-label="Copy intent ID"
777
+ >
778
+ {intentIdCopied ? (
779
+ <Check className="w-3.5 h-3.5 text-emerald-500" />
780
+ ) : (
781
+ <Copy className="w-3.5 h-3.5 text-gray-500 dark:text-gray-400" />
782
+ )}
783
+ </button>
784
+ </div>
785
+ </div>
786
+ )}
787
+
742
788
  {/* Max Slippage */}
743
789
  {hasSlippage && (
744
790
  <div className="flex justify-between items-center">
@@ -14,6 +14,7 @@ import { formatElapsed } from "../../utils.js"
14
14
  import { ChainImage } from "./ChainImage.js"
15
15
  import { getChainInfo } from "../../chains.js"
16
16
  import { useMode } from "../hooks/useMode.js"
17
+ import { ScreenHeader } from "./ScreenHeader.js"
17
18
 
18
19
  interface ReceiptProps {
19
20
  onSendAnother: () => void
@@ -252,7 +253,8 @@ export const Receipt: React.FC<ReceiptProps> = ({
252
253
 
253
254
  if (!finalExplorerUrl && !showRefundInfo) {
254
255
  return (
255
- <div className="flex flex-col justify-center min-h-full space-y-6 pt-8">
256
+ <div className="flex flex-col justify-center min-h-full space-y-6">
257
+ <ScreenHeader />
256
258
  <div className="text-center">
257
259
  <div className={`mx-auto flex items-center justify-center mb-4`}>
258
260
  <div className="w-16 h-16 rounded-full flex items-center justify-center bg-red-100 dark:bg-red-900/20">
@@ -313,7 +315,8 @@ export const Receipt: React.FC<ReceiptProps> = ({
313
315
  }
314
316
 
315
317
  return (
316
- <div className="flex flex-col justify-center min-h-full space-y-6 pt-8">
318
+ <div className="flex flex-col justify-center min-h-full space-y-6">
319
+ <ScreenHeader />
317
320
  <div className="text-center mb-2">
318
321
  <div className={`mx-auto flex items-center justify-center`}>
319
322
  {showRefundInfo || showMetaTxError ? (
@@ -8,7 +8,7 @@ import { useModal } from "../widget.js"
8
8
 
9
9
  interface ScreenHeaderProps {
10
10
  onBack?: () => void
11
- headerContent: string | React.ReactNode
11
+ headerContent?: string | React.ReactNode
12
12
  rightSideContent?: React.ReactNode
13
13
  headerContentAlign?: "left" | "center"
14
14
  onDebugScreenSelect?: (screen: string) => void
@@ -47,13 +47,15 @@ export const ScreenHeader: React.FC<ScreenHeaderProps> = ({
47
47
  )}
48
48
 
49
49
  {/* Header content */}
50
- <h2
51
- className={`text-lg font-semibold ${
52
- headerContentAlign === "center" ? "text-center" : "text-left"
53
- } text-gray-900 dark:text-white`}
54
- >
55
- {headerContent}
56
- </h2>
50
+ {headerContent && (
51
+ <h2
52
+ className={`text-lg font-semibold ${
53
+ headerContentAlign === "center" ? "text-center" : "text-left"
54
+ } text-gray-900 dark:text-white`}
55
+ >
56
+ {headerContent}
57
+ </h2>
58
+ )}
57
59
  </div>
58
60
 
59
61
  {/* Right side - Content, Account Actions, and Close button */}
@@ -298,6 +298,16 @@ function ToastItem({ toast, onRemove }: ToastItemProps) {
298
298
  <p className="text-sm text-gray-600 dark:text-gray-300 leading-relaxed">
299
299
  {toast.message}
300
300
  </p>
301
+ {toast.type === "error" && (
302
+ <a
303
+ href="https://support.trails.build/en/"
304
+ target="_blank"
305
+ rel="noopener noreferrer"
306
+ className="mt-2 text-xs text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 underline inline-block"
307
+ >
308
+ Visit support
309
+ </a>
310
+ )}
301
311
  </div>
302
312
  <button
303
313
  type="button"