@formo/analytics-react-native 0.1.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 (247) hide show
  1. package/README.md +302 -0
  2. package/lib/commonjs/FormoAnalytics.js +526 -0
  3. package/lib/commonjs/FormoAnalytics.js.map +1 -0
  4. package/lib/commonjs/FormoAnalyticsProvider.js +265 -0
  5. package/lib/commonjs/FormoAnalyticsProvider.js.map +1 -0
  6. package/lib/commonjs/constants/config.js +69 -0
  7. package/lib/commonjs/constants/config.js.map +1 -0
  8. package/lib/commonjs/constants/events.js +30 -0
  9. package/lib/commonjs/constants/events.js.map +1 -0
  10. package/lib/commonjs/constants/index.js +39 -0
  11. package/lib/commonjs/constants/index.js.map +1 -0
  12. package/lib/commonjs/constants/storage.js +23 -0
  13. package/lib/commonjs/constants/storage.js.map +1 -0
  14. package/lib/commonjs/index.js +65 -0
  15. package/lib/commonjs/index.js.map +1 -0
  16. package/lib/commonjs/lib/consent/index.js +56 -0
  17. package/lib/commonjs/lib/consent/index.js.map +1 -0
  18. package/lib/commonjs/lib/event/EventFactory.js +493 -0
  19. package/lib/commonjs/lib/event/EventFactory.js.map +1 -0
  20. package/lib/commonjs/lib/event/EventManager.js +46 -0
  21. package/lib/commonjs/lib/event/EventManager.js.map +1 -0
  22. package/lib/commonjs/lib/event/EventQueue.js +290 -0
  23. package/lib/commonjs/lib/event/EventQueue.js.map +1 -0
  24. package/lib/commonjs/lib/event/index.js +50 -0
  25. package/lib/commonjs/lib/event/index.js.map +1 -0
  26. package/lib/commonjs/lib/event/types.js +6 -0
  27. package/lib/commonjs/lib/event/types.js.map +1 -0
  28. package/lib/commonjs/lib/lifecycle/index.js +196 -0
  29. package/lib/commonjs/lib/lifecycle/index.js.map +1 -0
  30. package/lib/commonjs/lib/logger/index.js +48 -0
  31. package/lib/commonjs/lib/logger/index.js.map +1 -0
  32. package/lib/commonjs/lib/session/index.js +109 -0
  33. package/lib/commonjs/lib/session/index.js.map +1 -0
  34. package/lib/commonjs/lib/storage/AsyncStorageAdapter.js +164 -0
  35. package/lib/commonjs/lib/storage/AsyncStorageAdapter.js.map +1 -0
  36. package/lib/commonjs/lib/storage/MemoryStorage.js +41 -0
  37. package/lib/commonjs/lib/storage/MemoryStorage.js.map +1 -0
  38. package/lib/commonjs/lib/storage/StorageBlueprint.js +24 -0
  39. package/lib/commonjs/lib/storage/StorageBlueprint.js.map +1 -0
  40. package/lib/commonjs/lib/storage/StorageManager.js +126 -0
  41. package/lib/commonjs/lib/storage/StorageManager.js.map +1 -0
  42. package/lib/commonjs/lib/storage/index.js +49 -0
  43. package/lib/commonjs/lib/storage/index.js.map +1 -0
  44. package/lib/commonjs/lib/storage/types.js +2 -0
  45. package/lib/commonjs/lib/storage/types.js.map +1 -0
  46. package/lib/commonjs/lib/wagmi/WagmiEventHandler.js +445 -0
  47. package/lib/commonjs/lib/wagmi/WagmiEventHandler.js.map +1 -0
  48. package/lib/commonjs/lib/wagmi/index.js +28 -0
  49. package/lib/commonjs/lib/wagmi/index.js.map +1 -0
  50. package/lib/commonjs/lib/wagmi/types.js +2 -0
  51. package/lib/commonjs/lib/wagmi/types.js.map +1 -0
  52. package/lib/commonjs/types/base.js +6 -0
  53. package/lib/commonjs/types/base.js.map +1 -0
  54. package/lib/commonjs/types/events.js +22 -0
  55. package/lib/commonjs/types/events.js.map +1 -0
  56. package/lib/commonjs/types/index.js +28 -0
  57. package/lib/commonjs/types/index.js.map +1 -0
  58. package/lib/commonjs/utils/address.js +82 -0
  59. package/lib/commonjs/utils/address.js.map +1 -0
  60. package/lib/commonjs/utils/hash.js +30 -0
  61. package/lib/commonjs/utils/hash.js.map +1 -0
  62. package/lib/commonjs/utils/helpers.js +116 -0
  63. package/lib/commonjs/utils/helpers.js.map +1 -0
  64. package/lib/commonjs/utils/index.js +61 -0
  65. package/lib/commonjs/utils/index.js.map +1 -0
  66. package/lib/commonjs/utils/timestamp.js +34 -0
  67. package/lib/commonjs/utils/timestamp.js.map +1 -0
  68. package/lib/commonjs/utils/trafficSource.js +147 -0
  69. package/lib/commonjs/utils/trafficSource.js.map +1 -0
  70. package/lib/commonjs/version.js +10 -0
  71. package/lib/commonjs/version.js.map +1 -0
  72. package/lib/module/FormoAnalytics.js +519 -0
  73. package/lib/module/FormoAnalytics.js.map +1 -0
  74. package/lib/module/FormoAnalyticsProvider.js +256 -0
  75. package/lib/module/FormoAnalyticsProvider.js.map +1 -0
  76. package/lib/module/constants/config.js +62 -0
  77. package/lib/module/constants/config.js.map +1 -0
  78. package/lib/module/constants/events.js +24 -0
  79. package/lib/module/constants/events.js.map +1 -0
  80. package/lib/module/constants/index.js +4 -0
  81. package/lib/module/constants/index.js.map +1 -0
  82. package/lib/module/constants/storage.js +17 -0
  83. package/lib/module/constants/storage.js.map +1 -0
  84. package/lib/module/index.js +51 -0
  85. package/lib/module/index.js.map +1 -0
  86. package/lib/module/lib/consent/index.js +49 -0
  87. package/lib/module/lib/consent/index.js.map +1 -0
  88. package/lib/module/lib/event/EventFactory.js +488 -0
  89. package/lib/module/lib/event/EventFactory.js.map +1 -0
  90. package/lib/module/lib/event/EventManager.js +41 -0
  91. package/lib/module/lib/event/EventManager.js.map +1 -0
  92. package/lib/module/lib/event/EventQueue.js +283 -0
  93. package/lib/module/lib/event/EventQueue.js.map +1 -0
  94. package/lib/module/lib/event/index.js +5 -0
  95. package/lib/module/lib/event/index.js.map +1 -0
  96. package/lib/module/lib/event/types.js +2 -0
  97. package/lib/module/lib/event/types.js.map +1 -0
  98. package/lib/module/lib/lifecycle/index.js +190 -0
  99. package/lib/module/lib/lifecycle/index.js.map +1 -0
  100. package/lib/module/lib/logger/index.js +42 -0
  101. package/lib/module/lib/logger/index.js.map +1 -0
  102. package/lib/module/lib/session/index.js +92 -0
  103. package/lib/module/lib/session/index.js.map +1 -0
  104. package/lib/module/lib/storage/AsyncStorageAdapter.js +158 -0
  105. package/lib/module/lib/storage/AsyncStorageAdapter.js.map +1 -0
  106. package/lib/module/lib/storage/MemoryStorage.js +35 -0
  107. package/lib/module/lib/storage/MemoryStorage.js.map +1 -0
  108. package/lib/module/lib/storage/StorageBlueprint.js +18 -0
  109. package/lib/module/lib/storage/StorageBlueprint.js.map +1 -0
  110. package/lib/module/lib/storage/StorageManager.js +115 -0
  111. package/lib/module/lib/storage/StorageManager.js.map +1 -0
  112. package/lib/module/lib/storage/index.js +5 -0
  113. package/lib/module/lib/storage/index.js.map +1 -0
  114. package/lib/module/lib/storage/types.js +2 -0
  115. package/lib/module/lib/storage/types.js.map +1 -0
  116. package/lib/module/lib/wagmi/WagmiEventHandler.js +439 -0
  117. package/lib/module/lib/wagmi/WagmiEventHandler.js.map +1 -0
  118. package/lib/module/lib/wagmi/index.js +3 -0
  119. package/lib/module/lib/wagmi/index.js.map +1 -0
  120. package/lib/module/lib/wagmi/types.js +2 -0
  121. package/lib/module/lib/wagmi/types.js.map +1 -0
  122. package/lib/module/types/base.js +2 -0
  123. package/lib/module/types/base.js.map +1 -0
  124. package/lib/module/types/events.js +17 -0
  125. package/lib/module/types/events.js.map +1 -0
  126. package/lib/module/types/index.js +3 -0
  127. package/lib/module/types/index.js.map +1 -0
  128. package/lib/module/utils/address.js +74 -0
  129. package/lib/module/utils/address.js.map +1 -0
  130. package/lib/module/utils/hash.js +24 -0
  131. package/lib/module/utils/hash.js.map +1 -0
  132. package/lib/module/utils/helpers.js +105 -0
  133. package/lib/module/utils/helpers.js.map +1 -0
  134. package/lib/module/utils/index.js +6 -0
  135. package/lib/module/utils/index.js.map +1 -0
  136. package/lib/module/utils/timestamp.js +26 -0
  137. package/lib/module/utils/timestamp.js.map +1 -0
  138. package/lib/module/utils/trafficSource.js +137 -0
  139. package/lib/module/utils/trafficSource.js.map +1 -0
  140. package/lib/module/version.js +4 -0
  141. package/lib/module/version.js.map +1 -0
  142. package/lib/typescript/FormoAnalytics.d.ts +163 -0
  143. package/lib/typescript/FormoAnalytics.d.ts.map +1 -0
  144. package/lib/typescript/FormoAnalyticsProvider.d.ts +29 -0
  145. package/lib/typescript/FormoAnalyticsProvider.d.ts.map +1 -0
  146. package/lib/typescript/constants/config.d.ts +8 -0
  147. package/lib/typescript/constants/config.d.ts.map +1 -0
  148. package/lib/typescript/constants/events.d.ts +23 -0
  149. package/lib/typescript/constants/events.d.ts.map +1 -0
  150. package/lib/typescript/constants/index.d.ts +4 -0
  151. package/lib/typescript/constants/index.d.ts.map +1 -0
  152. package/lib/typescript/constants/storage.d.ts +10 -0
  153. package/lib/typescript/constants/storage.d.ts.map +1 -0
  154. package/lib/typescript/index.d.ts +44 -0
  155. package/lib/typescript/index.d.ts.map +1 -0
  156. package/lib/typescript/lib/consent/index.d.ts +13 -0
  157. package/lib/typescript/lib/consent/index.d.ts.map +1 -0
  158. package/lib/typescript/lib/event/EventFactory.d.ts +61 -0
  159. package/lib/typescript/lib/event/EventFactory.d.ts.map +1 -0
  160. package/lib/typescript/lib/event/EventManager.d.ts +17 -0
  161. package/lib/typescript/lib/event/EventManager.d.ts.map +1 -0
  162. package/lib/typescript/lib/event/EventQueue.d.ts +74 -0
  163. package/lib/typescript/lib/event/EventQueue.d.ts.map +1 -0
  164. package/lib/typescript/lib/event/index.d.ts +5 -0
  165. package/lib/typescript/lib/event/index.d.ts.map +1 -0
  166. package/lib/typescript/lib/event/types.d.ts +23 -0
  167. package/lib/typescript/lib/event/types.d.ts.map +1 -0
  168. package/lib/typescript/lib/lifecycle/index.d.ts +46 -0
  169. package/lib/typescript/lib/lifecycle/index.d.ts.map +1 -0
  170. package/lib/typescript/lib/logger/index.d.ts +19 -0
  171. package/lib/typescript/lib/logger/index.d.ts.map +1 -0
  172. package/lib/typescript/lib/session/index.d.ts +41 -0
  173. package/lib/typescript/lib/session/index.d.ts.map +1 -0
  174. package/lib/typescript/lib/storage/AsyncStorageAdapter.d.ts +48 -0
  175. package/lib/typescript/lib/storage/AsyncStorageAdapter.d.ts.map +1 -0
  176. package/lib/typescript/lib/storage/MemoryStorage.d.ts +18 -0
  177. package/lib/typescript/lib/storage/MemoryStorage.d.ts.map +1 -0
  178. package/lib/typescript/lib/storage/StorageBlueprint.d.ts +21 -0
  179. package/lib/typescript/lib/storage/StorageBlueprint.d.ts.map +1 -0
  180. package/lib/typescript/lib/storage/StorageManager.d.ts +45 -0
  181. package/lib/typescript/lib/storage/StorageManager.d.ts.map +1 -0
  182. package/lib/typescript/lib/storage/index.d.ts +5 -0
  183. package/lib/typescript/lib/storage/index.d.ts.map +1 -0
  184. package/lib/typescript/lib/storage/types.d.ts +22 -0
  185. package/lib/typescript/lib/storage/types.d.ts.map +1 -0
  186. package/lib/typescript/lib/wagmi/WagmiEventHandler.d.ts +104 -0
  187. package/lib/typescript/lib/wagmi/WagmiEventHandler.d.ts.map +1 -0
  188. package/lib/typescript/lib/wagmi/index.d.ts +3 -0
  189. package/lib/typescript/lib/wagmi/index.d.ts.map +1 -0
  190. package/lib/typescript/lib/wagmi/types.d.ts +54 -0
  191. package/lib/typescript/lib/wagmi/types.d.ts.map +1 -0
  192. package/lib/typescript/types/base.d.ts +219 -0
  193. package/lib/typescript/types/base.d.ts.map +1 -0
  194. package/lib/typescript/types/events.d.ts +111 -0
  195. package/lib/typescript/types/events.d.ts.map +1 -0
  196. package/lib/typescript/types/index.d.ts +3 -0
  197. package/lib/typescript/types/index.d.ts.map +1 -0
  198. package/lib/typescript/utils/address.d.ts +25 -0
  199. package/lib/typescript/utils/address.d.ts.map +1 -0
  200. package/lib/typescript/utils/hash.d.ts +10 -0
  201. package/lib/typescript/utils/hash.d.ts.map +1 -0
  202. package/lib/typescript/utils/helpers.d.ts +26 -0
  203. package/lib/typescript/utils/helpers.d.ts.map +1 -0
  204. package/lib/typescript/utils/index.d.ts +6 -0
  205. package/lib/typescript/utils/index.d.ts.map +1 -0
  206. package/lib/typescript/utils/timestamp.d.ts +13 -0
  207. package/lib/typescript/utils/timestamp.d.ts.map +1 -0
  208. package/lib/typescript/utils/trafficSource.d.ts +30 -0
  209. package/lib/typescript/utils/trafficSource.d.ts.map +1 -0
  210. package/lib/typescript/version.d.ts +2 -0
  211. package/lib/typescript/version.d.ts.map +1 -0
  212. package/package.json +143 -0
  213. package/src/FormoAnalytics.ts +685 -0
  214. package/src/FormoAnalyticsProvider.tsx +296 -0
  215. package/src/constants/config.ts +62 -0
  216. package/src/constants/events.ts +26 -0
  217. package/src/constants/index.ts +3 -0
  218. package/src/constants/storage.ts +16 -0
  219. package/src/index.ts +55 -0
  220. package/src/lib/consent/index.ts +52 -0
  221. package/src/lib/event/EventFactory.ts +682 -0
  222. package/src/lib/event/EventManager.ts +50 -0
  223. package/src/lib/event/EventQueue.ts +371 -0
  224. package/src/lib/event/index.ts +4 -0
  225. package/src/lib/event/types.ts +107 -0
  226. package/src/lib/lifecycle/index.ts +215 -0
  227. package/src/lib/logger/index.ts +56 -0
  228. package/src/lib/session/index.ts +103 -0
  229. package/src/lib/storage/AsyncStorageAdapter.ts +173 -0
  230. package/src/lib/storage/MemoryStorage.ts +43 -0
  231. package/src/lib/storage/StorageBlueprint.ts +30 -0
  232. package/src/lib/storage/StorageManager.ts +121 -0
  233. package/src/lib/storage/index.ts +4 -0
  234. package/src/lib/storage/types.ts +23 -0
  235. package/src/lib/wagmi/WagmiEventHandler.ts +574 -0
  236. package/src/lib/wagmi/index.ts +2 -0
  237. package/src/lib/wagmi/types.ts +71 -0
  238. package/src/types/base.ts +287 -0
  239. package/src/types/events.ts +140 -0
  240. package/src/types/index.ts +2 -0
  241. package/src/utils/address.ts +84 -0
  242. package/src/utils/hash.ts +23 -0
  243. package/src/utils/helpers.ts +139 -0
  244. package/src/utils/index.ts +5 -0
  245. package/src/utils/timestamp.ts +25 -0
  246. package/src/utils/trafficSource.ts +153 -0
  247. package/src/version.ts +3 -0
@@ -0,0 +1,574 @@
1
+ /**
2
+ * WagmiEventHandler for React Native
3
+ *
4
+ * Handles wallet event tracking by hooking into Wagmi v2's config.subscribe()
5
+ * and TanStack Query's MutationCache.
6
+ */
7
+
8
+ import { SignatureStatus, TransactionStatus } from "../../types/events";
9
+ import { logger } from "../logger";
10
+ import {
11
+ WagmiConfig,
12
+ WagmiState,
13
+ QueryClient,
14
+ MutationCacheEvent,
15
+ UnsubscribeFn,
16
+ WagmiTrackingState,
17
+ WagmiMutationKey,
18
+ } from "./types";
19
+
20
+ // Interface for FormoAnalytics to avoid circular dependency
21
+ interface IFormoAnalyticsInstance {
22
+ connect(
23
+ params: { chainId: number; address: string },
24
+ properties?: Record<string, unknown>
25
+ ): Promise<void>;
26
+ disconnect(params?: {
27
+ chainId?: number;
28
+ address?: string;
29
+ }): Promise<void>;
30
+ chain(params: { chainId: number; address?: string }): Promise<void>;
31
+ signature(params: {
32
+ status: SignatureStatus;
33
+ chainId: number;
34
+ address: string;
35
+ message: string;
36
+ signatureHash?: string;
37
+ }): Promise<void>;
38
+ transaction(params: {
39
+ status: TransactionStatus;
40
+ chainId: number;
41
+ address: string;
42
+ data?: string;
43
+ to?: string;
44
+ value?: string;
45
+ transactionHash?: string;
46
+ }): Promise<void>;
47
+ isAutocaptureEnabled(
48
+ eventType: "connect" | "disconnect" | "signature" | "transaction" | "chain"
49
+ ): boolean;
50
+ }
51
+
52
+ export class WagmiEventHandler {
53
+ private formo: IFormoAnalyticsInstance;
54
+ private wagmiConfig: WagmiConfig;
55
+ private queryClient?: QueryClient;
56
+ private unsubscribers: UnsubscribeFn[] = [];
57
+ private trackingState: WagmiTrackingState = {
58
+ isProcessing: false,
59
+ };
60
+ private processedMutations = new Set<string>();
61
+ private pendingStatusChanges: Array<{
62
+ status: WagmiState["status"];
63
+ prevStatus: WagmiState["status"];
64
+ }> = [];
65
+
66
+ constructor(
67
+ formoAnalytics: IFormoAnalyticsInstance,
68
+ wagmiConfig: WagmiConfig,
69
+ queryClient?: QueryClient
70
+ ) {
71
+ this.formo = formoAnalytics;
72
+ this.wagmiConfig = wagmiConfig;
73
+ this.queryClient = queryClient;
74
+
75
+ logger.info("WagmiEventHandler: Initializing Wagmi integration");
76
+
77
+ this.setupConnectionListeners();
78
+
79
+ if (this.queryClient) {
80
+ this.setupMutationTracking();
81
+ } else {
82
+ logger.warn(
83
+ "WagmiEventHandler: QueryClient not provided, signature and transaction events will not be tracked"
84
+ );
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Set up listeners for wallet connection, disconnection, and chain changes
90
+ */
91
+ private setupConnectionListeners(): void {
92
+ logger.info("WagmiEventHandler: Setting up connection listeners");
93
+
94
+ // Subscribe to status changes
95
+ const statusUnsubscribe = this.wagmiConfig.subscribe(
96
+ (state: WagmiState) => state.status,
97
+ (status, prevStatus) => {
98
+ this.handleStatusChange(status, prevStatus);
99
+ }
100
+ );
101
+ this.unsubscribers.push(statusUnsubscribe);
102
+
103
+ // Subscribe to chain ID changes
104
+ const chainIdUnsubscribe = this.wagmiConfig.subscribe(
105
+ (state: WagmiState) => state.chainId,
106
+ (chainId, prevChainId) => {
107
+ this.handleChainChange(chainId, prevChainId);
108
+ }
109
+ );
110
+ this.unsubscribers.push(chainIdUnsubscribe);
111
+
112
+ logger.info("WagmiEventHandler: Connection listeners set up successfully");
113
+ }
114
+
115
+ // Maximum pending status changes to prevent unbounded queue growth
116
+ private static readonly MAX_PENDING_STATUS_CHANGES = 10;
117
+
118
+ /**
119
+ * Handle status changes (connect/disconnect)
120
+ */
121
+ private async handleStatusChange(
122
+ status: WagmiState["status"],
123
+ prevStatus: WagmiState["status"]
124
+ ): Promise<void> {
125
+ if (this.trackingState.isProcessing) {
126
+ // Limit queue size to prevent unbounded growth during rapid status changes
127
+ if (this.pendingStatusChanges.length >= WagmiEventHandler.MAX_PENDING_STATUS_CHANGES) {
128
+ logger.warn("WagmiEventHandler: Pending status change queue full, dropping oldest");
129
+ this.pendingStatusChanges.shift();
130
+ }
131
+ // Queue status change to process after current one completes
132
+ this.pendingStatusChanges.push({ status, prevStatus });
133
+ logger.debug(
134
+ "WagmiEventHandler: Queuing status change for later processing"
135
+ );
136
+ return;
137
+ }
138
+
139
+ this.trackingState.isProcessing = true;
140
+
141
+ try {
142
+ // Process current status change
143
+ await this.processStatusChange(status, prevStatus);
144
+
145
+ // Process pending status changes iteratively (inline, no recursion)
146
+ while (this.pendingStatusChanges.length > 0) {
147
+ const pending = this.pendingStatusChanges.shift()!;
148
+ await this.processStatusChange(pending.status, pending.prevStatus);
149
+ }
150
+ } finally {
151
+ this.trackingState.isProcessing = false;
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Process a single status change (extracted for iterative processing)
157
+ */
158
+ private async processStatusChange(
159
+ status: WagmiState["status"],
160
+ prevStatus: WagmiState["status"]
161
+ ): Promise<void> {
162
+ try {
163
+ const state = this.getState();
164
+ const address = this.getConnectedAddress(state);
165
+ const chainId = state.chainId;
166
+
167
+ logger.info("WagmiEventHandler: Status changed", {
168
+ status,
169
+ prevStatus,
170
+ address,
171
+ chainId,
172
+ });
173
+
174
+ // Handle disconnect
175
+ if (status === "disconnected" && prevStatus === "connected") {
176
+ if (this.formo.isAutocaptureEnabled("disconnect")) {
177
+ await this.formo.disconnect({
178
+ chainId: this.trackingState.lastChainId,
179
+ address: this.trackingState.lastAddress,
180
+ });
181
+ }
182
+ this.trackingState.lastAddress = undefined;
183
+ this.trackingState.lastChainId = undefined;
184
+ }
185
+
186
+ // Handle connect
187
+ if (status === "connected" && prevStatus !== "connected") {
188
+ if (address && chainId !== undefined) {
189
+ this.trackingState.lastAddress = address;
190
+ this.trackingState.lastChainId = chainId;
191
+
192
+ if (this.formo.isAutocaptureEnabled("connect")) {
193
+ const connectorName = this.getConnectorName(state);
194
+ const connectorId = this.getConnectorId(state);
195
+ await this.formo.connect(
196
+ { chainId, address },
197
+ {
198
+ ...(connectorName && { providerName: connectorName }),
199
+ // Connector ID is typically the rdns for EIP-6963 wallets
200
+ ...(connectorId && { rdns: connectorId }),
201
+ }
202
+ );
203
+ }
204
+ }
205
+ }
206
+
207
+ this.trackingState.lastStatus = status;
208
+ } catch (error) {
209
+ logger.error("WagmiEventHandler: Error handling status change:", error);
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Handle chain ID changes
215
+ */
216
+ private async handleChainChange(
217
+ chainId: number | undefined,
218
+ prevChainId: number | undefined
219
+ ): Promise<void> {
220
+ // Skip if no change, chainId is undefined, or this is initial connection (prevChainId undefined)
221
+ if (chainId === prevChainId || chainId === undefined || prevChainId === undefined) {
222
+ return;
223
+ }
224
+
225
+ const state = this.getState();
226
+ if (state.status !== "connected") {
227
+ return;
228
+ }
229
+
230
+ const address = this.getConnectedAddress(state);
231
+ if (!address) {
232
+ logger.warn("WagmiEventHandler: Chain changed but no address found");
233
+ return;
234
+ }
235
+
236
+ logger.info("WagmiEventHandler: Chain changed", {
237
+ chainId,
238
+ prevChainId,
239
+ address,
240
+ });
241
+
242
+ this.trackingState.lastChainId = chainId;
243
+
244
+ if (this.formo.isAutocaptureEnabled("chain")) {
245
+ try {
246
+ await this.formo.chain({ chainId, address });
247
+ } catch (error) {
248
+ logger.error("WagmiEventHandler: Error tracking chain change:", error);
249
+ }
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Set up mutation tracking for signatures and transactions
255
+ */
256
+ private setupMutationTracking(): void {
257
+ if (!this.queryClient) {
258
+ return;
259
+ }
260
+
261
+ logger.info("WagmiEventHandler: Setting up mutation tracking");
262
+
263
+ const mutationCache = this.queryClient.getMutationCache();
264
+ const unsubscribe = mutationCache.subscribe((event: MutationCacheEvent) => {
265
+ this.handleMutationEvent(event);
266
+ });
267
+
268
+ this.unsubscribers.push(unsubscribe);
269
+ logger.info("WagmiEventHandler: Mutation tracking set up successfully");
270
+ }
271
+
272
+ /**
273
+ * Handle mutation cache events
274
+ */
275
+ private handleMutationEvent(event: MutationCacheEvent): void {
276
+ if (event.type !== "updated") {
277
+ return;
278
+ }
279
+
280
+ const mutation = event.mutation;
281
+ const mutationKey = mutation.options.mutationKey;
282
+
283
+ if (!mutationKey || mutationKey.length === 0) {
284
+ return;
285
+ }
286
+
287
+ const mutationType = mutationKey[0] as string;
288
+ const state = mutation.state;
289
+
290
+ const mutationStateKey = `${mutation.mutationId}:${state.status}`;
291
+
292
+ if (this.processedMutations.has(mutationStateKey)) {
293
+ return;
294
+ }
295
+
296
+ this.processedMutations.add(mutationStateKey);
297
+
298
+ logger.debug("WagmiEventHandler: Mutation event", {
299
+ mutationType,
300
+ mutationId: mutation.mutationId,
301
+ status: state.status,
302
+ });
303
+
304
+ if (mutationType === "signMessage" || mutationType === "signTypedData") {
305
+ this.handleSignatureMutation(
306
+ mutationType as WagmiMutationKey,
307
+ mutation
308
+ );
309
+ }
310
+
311
+ if (
312
+ mutationType === "sendTransaction" ||
313
+ mutationType === "writeContract"
314
+ ) {
315
+ this.handleTransactionMutation(
316
+ mutationType as WagmiMutationKey,
317
+ mutation
318
+ );
319
+ }
320
+
321
+ // Cleanup old mutations
322
+ if (this.processedMutations.size > 1000) {
323
+ const entries = Array.from(this.processedMutations);
324
+ for (let i = 0; i < 500; i++) {
325
+ const entry = entries[i];
326
+ if (entry) {
327
+ this.processedMutations.delete(entry);
328
+ }
329
+ }
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Handle signature mutations
335
+ */
336
+ private handleSignatureMutation(
337
+ mutationType: WagmiMutationKey,
338
+ mutation: MutationCacheEvent["mutation"]
339
+ ): void {
340
+ if (!this.formo.isAutocaptureEnabled("signature")) {
341
+ return;
342
+ }
343
+
344
+ const state = mutation.state;
345
+ const variables = state.variables || {};
346
+ const chainId = this.trackingState.lastChainId;
347
+ const address = this.trackingState.lastAddress;
348
+
349
+ if (!address) {
350
+ logger.warn(
351
+ "WagmiEventHandler: Signature event but no address available"
352
+ );
353
+ return;
354
+ }
355
+
356
+ if (!chainId || chainId === 0) {
357
+ logger.warn(
358
+ "WagmiEventHandler: Signature event but no valid chainId available"
359
+ );
360
+ return;
361
+ }
362
+
363
+ try {
364
+ let status: SignatureStatus;
365
+ let signatureHash: string | undefined;
366
+
367
+ if (state.status === "pending") {
368
+ status = SignatureStatus.REQUESTED;
369
+ } else if (state.status === "success") {
370
+ status = SignatureStatus.CONFIRMED;
371
+ signatureHash = state.data as string;
372
+ } else if (state.status === "error") {
373
+ status = SignatureStatus.REJECTED;
374
+ } else {
375
+ return;
376
+ }
377
+
378
+ let message: string;
379
+ if (mutationType === "signMessage") {
380
+ message = (variables.message as string) || "";
381
+ } else {
382
+ message = JSON.stringify(variables.message || variables.types || {});
383
+ }
384
+
385
+ logger.info("WagmiEventHandler: Tracking signature event", {
386
+ status,
387
+ mutationType,
388
+ address,
389
+ chainId,
390
+ });
391
+
392
+ this.formo.signature({
393
+ status,
394
+ chainId,
395
+ address,
396
+ message,
397
+ ...(signatureHash && { signatureHash }),
398
+ }).catch((error) => {
399
+ logger.error("WagmiEventHandler: Error tracking signature:", error);
400
+ });
401
+ } catch (error) {
402
+ logger.error(
403
+ "WagmiEventHandler: Error handling signature mutation:",
404
+ error
405
+ );
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Handle transaction mutations
411
+ */
412
+ private handleTransactionMutation(
413
+ mutationType: WagmiMutationKey,
414
+ mutation: MutationCacheEvent["mutation"]
415
+ ): void {
416
+ if (!this.formo.isAutocaptureEnabled("transaction")) {
417
+ return;
418
+ }
419
+
420
+ const state = mutation.state;
421
+ const variables = state.variables || {};
422
+ const chainId =
423
+ this.trackingState.lastChainId ||
424
+ (variables.chainId as number | undefined);
425
+ // Only use variables.account as fallback, not variables.address which is the contract address
426
+ const address =
427
+ this.trackingState.lastAddress ||
428
+ (variables.account as string | undefined);
429
+
430
+ if (!address) {
431
+ logger.warn(
432
+ "WagmiEventHandler: Transaction event but no address available"
433
+ );
434
+ return;
435
+ }
436
+
437
+ if (!chainId || chainId === 0) {
438
+ logger.warn(
439
+ "WagmiEventHandler: Transaction event but no valid chainId available"
440
+ );
441
+ return;
442
+ }
443
+
444
+ try {
445
+ let status: TransactionStatus;
446
+ let transactionHash: string | undefined;
447
+
448
+ if (state.status === "pending") {
449
+ status = TransactionStatus.STARTED;
450
+ } else if (state.status === "success") {
451
+ status = TransactionStatus.BROADCASTED;
452
+ transactionHash = state.data as string;
453
+ } else if (state.status === "error") {
454
+ status = TransactionStatus.REJECTED;
455
+ } else {
456
+ return;
457
+ }
458
+
459
+ const data = variables.data as string | undefined;
460
+ const to =
461
+ (variables.to as string | undefined) ||
462
+ (variables.address as string | undefined);
463
+ const value = variables.value?.toString();
464
+
465
+ logger.info("WagmiEventHandler: Tracking transaction event", {
466
+ status,
467
+ mutationType,
468
+ address,
469
+ chainId,
470
+ transactionHash,
471
+ });
472
+
473
+ this.formo.transaction({
474
+ status,
475
+ chainId,
476
+ address,
477
+ ...(data && { data }),
478
+ ...(to && { to }),
479
+ ...(value && { value }),
480
+ ...(transactionHash && { transactionHash }),
481
+ }).catch((error) => {
482
+ logger.error("WagmiEventHandler: Error tracking transaction:", error);
483
+ });
484
+ } catch (error) {
485
+ logger.error(
486
+ "WagmiEventHandler: Error handling transaction mutation:",
487
+ error
488
+ );
489
+ }
490
+ }
491
+
492
+ /**
493
+ * Get current Wagmi state
494
+ */
495
+ private getState(): WagmiState {
496
+ if (typeof this.wagmiConfig.getState === "function") {
497
+ return this.wagmiConfig.getState();
498
+ }
499
+
500
+ if (this.wagmiConfig.state) {
501
+ return this.wagmiConfig.state;
502
+ }
503
+
504
+ logger.warn(
505
+ "WagmiEventHandler: Unable to get state from config, returning default state"
506
+ );
507
+ return {
508
+ status: "disconnected",
509
+ connections: new Map(),
510
+ current: undefined,
511
+ chainId: undefined,
512
+ };
513
+ }
514
+
515
+ /**
516
+ * Get connected address from state
517
+ */
518
+ private getConnectedAddress(state: WagmiState): string | undefined {
519
+ if (!state.current) {
520
+ return undefined;
521
+ }
522
+
523
+ const connection = state.connections.get(state.current);
524
+ if (!connection || connection.accounts.length === 0) {
525
+ return undefined;
526
+ }
527
+
528
+ return connection.accounts[0];
529
+ }
530
+
531
+ /**
532
+ * Get connector name from state
533
+ */
534
+ private getConnectorName(state: WagmiState): string | undefined {
535
+ if (!state.current) {
536
+ return undefined;
537
+ }
538
+
539
+ const connection = state.connections.get(state.current);
540
+ return connection?.connector.name;
541
+ }
542
+
543
+ /**
544
+ * Get connector ID from state (typically the rdns for EIP-6963 wallets)
545
+ */
546
+ private getConnectorId(state: WagmiState): string | undefined {
547
+ if (!state.current) {
548
+ return undefined;
549
+ }
550
+
551
+ const connection = state.connections.get(state.current);
552
+ return connection?.connector.id;
553
+ }
554
+
555
+ /**
556
+ * Clean up subscriptions
557
+ */
558
+ public cleanup(): void {
559
+ logger.info("WagmiEventHandler: Cleaning up subscriptions");
560
+
561
+ for (const unsubscribe of this.unsubscribers) {
562
+ try {
563
+ unsubscribe();
564
+ } catch (error) {
565
+ logger.error("WagmiEventHandler: Error during cleanup:", error);
566
+ }
567
+ }
568
+
569
+ this.unsubscribers = [];
570
+ this.processedMutations.clear();
571
+ this.pendingStatusChanges = [];
572
+ logger.info("WagmiEventHandler: Cleanup complete");
573
+ }
574
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./WagmiEventHandler";
2
+ export * from "./types";
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Wagmi types for React Native SDK
3
+ */
4
+
5
+ export interface WagmiState {
6
+ status: "connecting" | "connected" | "disconnected" | "reconnecting";
7
+ connections: Map<
8
+ string,
9
+ {
10
+ accounts: readonly string[];
11
+ chainId: number;
12
+ connector: {
13
+ name: string;
14
+ /** Connector ID - for EIP-6963 injected wallets, this is typically the rdns */
15
+ id: string;
16
+ type?: string;
17
+ icon?: string;
18
+ };
19
+ }
20
+ >;
21
+ current?: string;
22
+ chainId?: number;
23
+ }
24
+
25
+ export interface WagmiConfig {
26
+ subscribe: <T>(
27
+ selector: (state: WagmiState) => T,
28
+ listener: (selectedState: T, prevSelectedState: T) => void
29
+ ) => () => void;
30
+ getState?: () => WagmiState;
31
+ state?: WagmiState;
32
+ }
33
+
34
+ export interface QueryClient {
35
+ getMutationCache: () => MutationCache;
36
+ }
37
+
38
+ export interface MutationCache {
39
+ subscribe: (callback: (event: MutationCacheEvent) => void) => () => void;
40
+ }
41
+
42
+ export interface MutationCacheEvent {
43
+ type: "added" | "updated" | "removed";
44
+ mutation: {
45
+ mutationId: number;
46
+ options: {
47
+ mutationKey?: readonly string[];
48
+ };
49
+ state: {
50
+ status: "idle" | "pending" | "success" | "error";
51
+ data?: unknown;
52
+ error?: unknown;
53
+ variables?: Record<string, unknown>;
54
+ };
55
+ };
56
+ }
57
+
58
+ export type UnsubscribeFn = () => void;
59
+
60
+ export interface WagmiTrackingState {
61
+ isProcessing: boolean;
62
+ lastAddress?: string;
63
+ lastChainId?: number;
64
+ lastStatus?: WagmiState["status"];
65
+ }
66
+
67
+ export type WagmiMutationKey =
68
+ | "signMessage"
69
+ | "signTypedData"
70
+ | "sendTransaction"
71
+ | "writeContract";