@nadohq/mobile-client 0.64.0 → 0.66.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 (225) hide show
  1. package/README.md +124 -7
  2. package/dist/MobileClient-M4oC4lrX.cjs +1101 -0
  3. package/dist/MobileClient-M4oC4lrX.cjs.map +1 -0
  4. package/dist/MobileClient.cjs +1 -1
  5. package/dist/MobileClient.d.cts +306 -10
  6. package/dist/MobileClient.d.cts.map +1 -1
  7. package/dist/MobileClient.d.ts +306 -10
  8. package/dist/MobileClient.d.ts.map +1 -1
  9. package/dist/MobileClient.js +544 -5
  10. package/dist/MobileClient.js.map +1 -1
  11. package/dist/{MobileServerFailureError-Dd3c6P_Z.d.ts → MobileServerFailureError-C9iqNsxI.d.ts} +2 -3
  12. package/dist/MobileServerFailureError-C9iqNsxI.d.ts.map +1 -0
  13. package/dist/{MobileServerFailureError-Bpa_WA9f.d.cts → MobileServerFailureError-MtS2reel.d.cts} +2 -3
  14. package/dist/MobileServerFailureError-MtS2reel.d.cts.map +1 -0
  15. package/dist/MobileUnsupportedFeedItemError-BsZvW8i2.d.cts +15 -0
  16. package/dist/MobileUnsupportedFeedItemError-BsZvW8i2.d.cts.map +1 -0
  17. package/dist/MobileUnsupportedFeedItemError-BsZvW8i2.d.ts +15 -0
  18. package/dist/MobileUnsupportedFeedItemError-BsZvW8i2.d.ts.map +1 -0
  19. package/dist/MobileUnsupportedNotificationRouteError-DpiNBvvY.d.cts +15 -0
  20. package/dist/MobileUnsupportedNotificationRouteError-DpiNBvvY.d.cts.map +1 -0
  21. package/dist/MobileUnsupportedNotificationRouteError-DpiNBvvY.d.ts +15 -0
  22. package/dist/MobileUnsupportedNotificationRouteError-DpiNBvvY.d.ts.map +1 -0
  23. package/dist/{buildSignedMobileRequest-Cb1T74xV.d.ts → buildSignedMobileRequest-BR-KJhIF.d.ts} +2 -2
  24. package/dist/{buildSignedMobileRequest-Cb1T74xV.d.ts.map → buildSignedMobileRequest-BR-KJhIF.d.ts.map} +1 -1
  25. package/dist/{buildSignedMobileRequest-NHbMAPzd.d.cts → buildSignedMobileRequest-DyKCK34Y.d.cts} +2 -2
  26. package/dist/{buildSignedMobileRequest-NHbMAPzd.d.cts.map → buildSignedMobileRequest-DyKCK34Y.d.cts.map} +1 -1
  27. package/dist/{canonicalize-BBYGh17w.d.ts → canonicalize-Bv6UIAbR.d.ts} +2 -2
  28. package/dist/canonicalize-Bv6UIAbR.d.ts.map +1 -0
  29. package/dist/{canonicalize-BF_SiNvO.d.cts → canonicalize-CWh3XZzj.d.cts} +2 -2
  30. package/dist/canonicalize-CWh3XZzj.d.cts.map +1 -0
  31. package/dist/clientTypes-BUn5_OzK.d.ts +1665 -0
  32. package/dist/clientTypes-BUn5_OzK.d.ts.map +1 -0
  33. package/dist/clientTypes-DOpUcu80.d.cts +1665 -0
  34. package/dist/clientTypes-DOpUcu80.d.cts.map +1 -0
  35. package/dist/dataMappers.cjs +691 -14
  36. package/dist/dataMappers.cjs.map +1 -1
  37. package/dist/dataMappers.d.cts +254 -8
  38. package/dist/dataMappers.d.cts.map +1 -1
  39. package/dist/dataMappers.d.ts +254 -8
  40. package/dist/dataMappers.d.ts.map +1 -1
  41. package/dist/dataMappers.js +651 -15
  42. package/dist/dataMappers.js.map +1 -1
  43. package/dist/{eip712MethodByType-BSx11xG-.d.cts → eip712MethodByType-CJ6BoayT.d.cts} +2 -2
  44. package/dist/eip712MethodByType-CJ6BoayT.d.cts.map +1 -0
  45. package/dist/{eip712MethodByType-DkiAjCLs.d.ts → eip712MethodByType-Dx4mvgni.d.ts} +2 -2
  46. package/dist/eip712MethodByType-Dx4mvgni.d.ts.map +1 -0
  47. package/dist/{feed-DTJlsg8W.d.ts → feed-DErOLaoY.d.cts} +4 -2
  48. package/dist/feed-DErOLaoY.d.cts.map +1 -0
  49. package/dist/{feed-DTJlsg8W.d.cts → feed-DErOLaoY.d.ts} +4 -2
  50. package/dist/feed-DErOLaoY.d.ts.map +1 -0
  51. package/dist/index.cjs +9 -2
  52. package/dist/index.d.cts +15 -16
  53. package/dist/index.d.ts +15 -16
  54. package/dist/index.js +5 -3
  55. package/dist/{mobileErrorCodes-3LtreeZM.d.ts → mobileErrorCodes-vD6rbFuF.d.cts} +25 -6
  56. package/dist/mobileErrorCodes-vD6rbFuF.d.cts.map +1 -0
  57. package/dist/{mobileErrorCodes-3LtreeZM.d.cts → mobileErrorCodes-vD6rbFuF.d.ts} +25 -6
  58. package/dist/mobileErrorCodes-vD6rbFuF.d.ts.map +1 -0
  59. package/dist/{payloadHash-BOWGjqdb.d.cts → payloadHash-CdkF2Ocr.d.cts} +2 -2
  60. package/dist/payloadHash-CdkF2Ocr.d.cts.map +1 -0
  61. package/dist/{payloadHash-Bzf4CtjV.d.ts → payloadHash-HD29K77M.d.ts} +2 -2
  62. package/dist/payloadHash-HD29K77M.d.ts.map +1 -0
  63. package/dist/serverExecuteTypes-5JyyIXRK.d.ts +178 -0
  64. package/dist/serverExecuteTypes-5JyyIXRK.d.ts.map +1 -0
  65. package/dist/serverExecuteTypes-CHrQ9Rtq.d.cts +178 -0
  66. package/dist/serverExecuteTypes-CHrQ9Rtq.d.cts.map +1 -0
  67. package/dist/serverModelTypes-CGUZCi0y.d.cts +938 -0
  68. package/dist/serverModelTypes-CGUZCi0y.d.cts.map +1 -0
  69. package/dist/serverModelTypes-CGUZCi0y.d.ts +938 -0
  70. package/dist/serverModelTypes-CGUZCi0y.d.ts.map +1 -0
  71. package/dist/{serverResponseGuards-BU1Xv_tG.d.ts → serverResponseGuards-Bvw8ltYJ.d.ts} +2 -2
  72. package/dist/{serverResponseGuards-BU1Xv_tG.d.ts.map → serverResponseGuards-Bvw8ltYJ.d.ts.map} +1 -1
  73. package/dist/{serverResponseGuards-Dkdok0Sm.d.cts → serverResponseGuards-i1njcYKN.d.cts} +2 -2
  74. package/dist/{serverResponseGuards-Dkdok0Sm.d.cts.map → serverResponseGuards-i1njcYKN.d.cts.map} +1 -1
  75. package/dist/signing/buildSignedMobileRequest.d.cts +1 -1
  76. package/dist/signing/buildSignedMobileRequest.d.ts +1 -1
  77. package/dist/signing/canonicalize.cjs +86 -0
  78. package/dist/signing/canonicalize.cjs.map +1 -1
  79. package/dist/signing/canonicalize.d.cts +1 -1
  80. package/dist/signing/canonicalize.d.ts +1 -1
  81. package/dist/signing/canonicalize.js +86 -0
  82. package/dist/signing/canonicalize.js.map +1 -1
  83. package/dist/signing/eip712MethodByType.cjs +12 -0
  84. package/dist/signing/eip712MethodByType.cjs.map +1 -1
  85. package/dist/signing/eip712MethodByType.d.cts +1 -1
  86. package/dist/signing/eip712MethodByType.d.ts +1 -1
  87. package/dist/signing/eip712MethodByType.js +12 -0
  88. package/dist/signing/eip712MethodByType.js.map +1 -1
  89. package/dist/signing/index.d.cts +5 -5
  90. package/dist/signing/index.d.ts +5 -5
  91. package/dist/signing/payloadHash.cjs +1 -1
  92. package/dist/signing/payloadHash.d.cts +1 -1
  93. package/dist/signing/payloadHash.d.ts +1 -1
  94. package/dist/signing/payloadHash.js +1 -1
  95. package/dist/signing/types.d.cts +1 -1
  96. package/dist/signing/types.d.ts +1 -1
  97. package/dist/types/MobileServerFailureError.d.cts +1 -1
  98. package/dist/types/MobileServerFailureError.d.ts +1 -1
  99. package/dist/types/MobileUnsupportedFeedItemError.cjs +19 -0
  100. package/dist/types/MobileUnsupportedFeedItemError.cjs.map +1 -0
  101. package/dist/types/MobileUnsupportedFeedItemError.d.cts +2 -0
  102. package/dist/types/MobileUnsupportedFeedItemError.d.ts +2 -0
  103. package/dist/types/MobileUnsupportedFeedItemError.js +18 -0
  104. package/dist/types/MobileUnsupportedFeedItemError.js.map +1 -0
  105. package/dist/types/MobileUnsupportedNotificationRouteError.cjs +19 -0
  106. package/dist/types/MobileUnsupportedNotificationRouteError.cjs.map +1 -0
  107. package/dist/types/MobileUnsupportedNotificationRouteError.d.cts +2 -0
  108. package/dist/types/MobileUnsupportedNotificationRouteError.d.ts +2 -0
  109. package/dist/types/MobileUnsupportedNotificationRouteError.js +18 -0
  110. package/dist/types/MobileUnsupportedNotificationRouteError.js.map +1 -0
  111. package/dist/types/clientTypes.d.cts +2 -2
  112. package/dist/types/clientTypes.d.ts +2 -2
  113. package/dist/types/index.cjs +6 -0
  114. package/dist/types/index.d.cts +9 -9
  115. package/dist/types/index.d.ts +9 -9
  116. package/dist/types/index.js +4 -2
  117. package/dist/types/mobileErrorCodes.cjs +39 -1
  118. package/dist/types/mobileErrorCodes.cjs.map +1 -1
  119. package/dist/types/mobileErrorCodes.d.cts +1 -1
  120. package/dist/types/mobileErrorCodes.d.ts +1 -1
  121. package/dist/types/mobileErrorCodes.js +39 -1
  122. package/dist/types/mobileErrorCodes.js.map +1 -1
  123. package/dist/types/serverBaseTypes.d.cts +1 -1
  124. package/dist/types/serverBaseTypes.d.ts +1 -1
  125. package/dist/types/serverExecuteTypes.d.cts +2 -2
  126. package/dist/types/serverExecuteTypes.d.ts +2 -2
  127. package/dist/types/serverModelTypes.cjs +15 -0
  128. package/dist/types/serverModelTypes.cjs.map +1 -1
  129. package/dist/types/serverModelTypes.d.cts +2 -2
  130. package/dist/types/serverModelTypes.d.ts +2 -2
  131. package/dist/types/serverModelTypes.js +14 -1
  132. package/dist/types/serverModelTypes.js.map +1 -1
  133. package/dist/types/serverQueryTypes.d.cts +2 -2
  134. package/dist/types/serverQueryTypes.d.ts +2 -2
  135. package/dist/types/serverRequestTypes.d.cts +2 -2
  136. package/dist/types/serverRequestTypes.d.ts +2 -2
  137. package/dist/types-C9YE4EIt.d.cts +794 -0
  138. package/dist/types-C9YE4EIt.d.cts.map +1 -0
  139. package/dist/types-Cwy1y_9x.d.ts +794 -0
  140. package/dist/types-Cwy1y_9x.d.ts.map +1 -0
  141. package/dist/utils/buildMobileFeedImageMultipart.cjs +31 -0
  142. package/dist/utils/buildMobileFeedImageMultipart.cjs.map +1 -0
  143. package/dist/utils/buildMobileFeedImageMultipart.d.cts +15 -0
  144. package/dist/utils/buildMobileFeedImageMultipart.d.cts.map +1 -0
  145. package/dist/utils/buildMobileFeedImageMultipart.d.ts +15 -0
  146. package/dist/utils/buildMobileFeedImageMultipart.d.ts.map +1 -0
  147. package/dist/utils/buildMobileFeedImageMultipart.js +30 -0
  148. package/dist/utils/buildMobileFeedImageMultipart.js.map +1 -0
  149. package/dist/utils/feed.cjs +3 -0
  150. package/dist/utils/feed.cjs.map +1 -1
  151. package/dist/utils/feed.d.cts +2 -2
  152. package/dist/utils/feed.d.ts +2 -2
  153. package/dist/utils/feed.js +3 -1
  154. package/dist/utils/feed.js.map +1 -1
  155. package/dist/utils/index.cjs +2 -1
  156. package/dist/utils/index.d.cts +3 -3
  157. package/dist/utils/index.d.ts +3 -3
  158. package/dist/utils/index.js +2 -2
  159. package/dist/utils/serverResponseGuards.d.cts +1 -1
  160. package/dist/utils/serverResponseGuards.d.ts +1 -1
  161. package/package.json +3 -3
  162. package/src/MobileClient.ts +789 -7
  163. package/src/dataMappers.test.ts +848 -0
  164. package/src/dataMappers.ts +924 -17
  165. package/src/feedImages.test.ts +309 -0
  166. package/src/notifications.test.ts +950 -0
  167. package/src/signing/canonicalize.ts +119 -0
  168. package/src/signing/eip712MethodByType.ts +15 -0
  169. package/src/signing/signing.test.ts +362 -0
  170. package/src/signing/types.ts +108 -13
  171. package/src/socialFeed.test.ts +38 -2
  172. package/src/types/MobileUnsupportedFeedItemError.ts +18 -0
  173. package/src/types/MobileUnsupportedNotificationRouteError.ts +16 -0
  174. package/src/types/clientTypes.ts +1012 -34
  175. package/src/types/index.ts +2 -0
  176. package/src/types/mobileErrorCodes.ts +38 -0
  177. package/src/types/serverExecuteTypes.ts +112 -3
  178. package/src/types/serverModelTypes.ts +581 -28
  179. package/src/types/serverQueryTypes.ts +212 -5
  180. package/src/types/serverRequestTypes.ts +213 -13
  181. package/src/utils/buildMobileFeedImageMultipart.test.ts +45 -0
  182. package/src/utils/buildMobileFeedImageMultipart.ts +36 -0
  183. package/src/utils/feed.ts +3 -0
  184. package/dist/MobileClient-aDxiM_Kf.cjs +0 -562
  185. package/dist/MobileClient-aDxiM_Kf.cjs.map +0 -1
  186. package/dist/MobileServerFailureError-Bpa_WA9f.d.cts.map +0 -1
  187. package/dist/MobileServerFailureError-Dd3c6P_Z.d.ts.map +0 -1
  188. package/dist/canonicalize-BBYGh17w.d.ts.map +0 -1
  189. package/dist/canonicalize-BF_SiNvO.d.cts.map +0 -1
  190. package/dist/clientTypes-BWFKo9oE.d.cts +0 -844
  191. package/dist/clientTypes-BWFKo9oE.d.cts.map +0 -1
  192. package/dist/clientTypes-DCTiUV0H.d.ts +0 -844
  193. package/dist/clientTypes-DCTiUV0H.d.ts.map +0 -1
  194. package/dist/eip712MethodByType-BSx11xG-.d.cts.map +0 -1
  195. package/dist/eip712MethodByType-DkiAjCLs.d.ts.map +0 -1
  196. package/dist/feed-DTJlsg8W.d.cts.map +0 -1
  197. package/dist/feed-DTJlsg8W.d.ts.map +0 -1
  198. package/dist/mobileErrorCodes-3LtreeZM.d.cts.map +0 -1
  199. package/dist/mobileErrorCodes-3LtreeZM.d.ts.map +0 -1
  200. package/dist/payloadHash-BOWGjqdb.d.cts.map +0 -1
  201. package/dist/payloadHash-Bzf4CtjV.d.ts.map +0 -1
  202. package/dist/serverBaseTypes-DWf3iGWy.d.ts +0 -25
  203. package/dist/serverBaseTypes-DWf3iGWy.d.ts.map +0 -1
  204. package/dist/serverBaseTypes-KREDe2M9.d.cts +0 -25
  205. package/dist/serverBaseTypes-KREDe2M9.d.cts.map +0 -1
  206. package/dist/serverExecuteTypes-DVRa6ifu.d.cts +0 -89
  207. package/dist/serverExecuteTypes-DVRa6ifu.d.cts.map +0 -1
  208. package/dist/serverExecuteTypes-DlYZCp1f.d.ts +0 -89
  209. package/dist/serverExecuteTypes-DlYZCp1f.d.ts.map +0 -1
  210. package/dist/serverModelTypes-DAqEyYtB.d.cts +0 -508
  211. package/dist/serverModelTypes-DAqEyYtB.d.cts.map +0 -1
  212. package/dist/serverModelTypes-DAqEyYtB.d.ts +0 -508
  213. package/dist/serverModelTypes-DAqEyYtB.d.ts.map +0 -1
  214. package/dist/serverQueryTypes-BFdwq0Z2.d.ts +0 -171
  215. package/dist/serverQueryTypes-BFdwq0Z2.d.ts.map +0 -1
  216. package/dist/serverQueryTypes-DNdMpmDr.d.cts +0 -171
  217. package/dist/serverQueryTypes-DNdMpmDr.d.cts.map +0 -1
  218. package/dist/serverRequestTypes-BNA7FCY6.d.ts +0 -200
  219. package/dist/serverRequestTypes-BNA7FCY6.d.ts.map +0 -1
  220. package/dist/serverRequestTypes-CxDRFKfv.d.cts +0 -200
  221. package/dist/serverRequestTypes-CxDRFKfv.d.cts.map +0 -1
  222. package/dist/types-BCr2G5gS.d.cts +0 -84
  223. package/dist/types-BCr2G5gS.d.cts.map +0 -1
  224. package/dist/types-C18k-9Vy.d.ts +0 -84
  225. package/dist/types-C18k-9Vy.d.ts.map +0 -1
@@ -1,3 +1,4 @@
1
+ import { MobileSocialFeedMention } from '../types/serverModelTypes';
1
2
  import { MobileSignedInner } from './types';
2
3
 
3
4
  /**
@@ -11,6 +12,113 @@ export function canonicalizeMobileInner(
11
12
  inner: MobileSignedInner,
12
13
  ): MobileSignedInner {
13
14
  switch (inner.type) {
15
+ case 'register_expo_token_v2':
16
+ return {
17
+ type: inner.type,
18
+ expo_token: inner.expo_token,
19
+ platform: inner.platform,
20
+ locale: inner.locale,
21
+ app_version: inner.app_version,
22
+ capabilities: {
23
+ payload_versions: [...inner.capabilities.payload_versions],
24
+ type_ids: [...inner.capabilities.type_ids],
25
+ routes: [...inner.capabilities.routes],
26
+ },
27
+ };
28
+ case 'notification_subscriptions':
29
+ return { type: inner.type };
30
+ case 'update_notification_subscriptions':
31
+ return {
32
+ type: inner.type,
33
+ expected_revision: inner.expected_revision,
34
+ trader_bell:
35
+ inner.trader_bell == null
36
+ ? null
37
+ : {
38
+ subaccount: inner.trader_bell.subaccount,
39
+ enabled: inner.trader_bell.enabled,
40
+ },
41
+ market:
42
+ inner.market == null
43
+ ? null
44
+ : {
45
+ product_id: inner.market.product_id,
46
+ subscribed: inner.market.subscribed,
47
+ },
48
+ import_markets:
49
+ inner.import_markets == null ? null : [...inner.import_markets],
50
+ };
51
+ case 'notification_inbox_v2':
52
+ return {
53
+ type: inner.type,
54
+ cursor: inner.cursor,
55
+ limit: inner.limit,
56
+ };
57
+ case 'record_product_events':
58
+ return {
59
+ type: inner.type,
60
+ events: inner.events.map((event) => ({
61
+ client_event_id: event.client_event_id,
62
+ event_name: event.event_name,
63
+ notification_id: event.notification_id,
64
+ occurred_at: event.occurred_at,
65
+ product_id: event.product_id,
66
+ query: event.query,
67
+ })),
68
+ };
69
+ case 'create_feed_post':
70
+ return {
71
+ type: inner.type,
72
+ client_request_id: inner.client_request_id,
73
+ position_id: inner.position_id,
74
+ ...(inner.action_id === undefined
75
+ ? {}
76
+ : { action_id: inner.action_id }),
77
+ text: inner.text,
78
+ mentions: inner.mentions.map(canonicalizeMention),
79
+ ...(inner.drawing_data_json === undefined
80
+ ? {}
81
+ : { drawing_data_json: inner.drawing_data_json }),
82
+ ...(inner.image_ids === undefined
83
+ ? {}
84
+ : { image_ids: [...inner.image_ids] }),
85
+ };
86
+ case 'create_feed_comment':
87
+ return {
88
+ type: inner.type,
89
+ client_request_id: inner.client_request_id,
90
+ root_item_id: inner.root_item_id,
91
+ ...(inner.reply_to_item_id === undefined
92
+ ? {}
93
+ : { reply_to_item_id: inner.reply_to_item_id }),
94
+ text: inner.text,
95
+ mentions: inner.mentions.map(canonicalizeMention),
96
+ ...(inner.attached_position_id === undefined
97
+ ? {}
98
+ : { attached_position_id: inner.attached_position_id }),
99
+ ...(inner.image_ids === undefined
100
+ ? {}
101
+ : { image_ids: [...inner.image_ids] }),
102
+ };
103
+ case 'create_feed_post_image_uploads':
104
+ case 'create_feed_comment_image_uploads':
105
+ return {
106
+ type: inner.type,
107
+ content_hash: inner.content_hash,
108
+ };
109
+ case 'create_feed_chart_drawing':
110
+ case 'update_feed_chart_drawing':
111
+ return {
112
+ type: inner.type,
113
+ item_id: inner.item_id,
114
+ data_json: inner.data_json,
115
+ };
116
+ case 'set_feed_like':
117
+ return {
118
+ type: inner.type,
119
+ item_id: inner.item_id,
120
+ is_liked: inner.is_liked,
121
+ };
14
122
  case 'set_bio':
15
123
  return { type: 'set_bio', bio: inner.bio };
16
124
  case 'create_avatar_upload':
@@ -62,3 +170,14 @@ export function canonicalizeMobileInner(
62
170
  }
63
171
  }
64
172
  }
173
+
174
+ function canonicalizeMention(
175
+ mention: MobileSocialFeedMention,
176
+ ): MobileSocialFeedMention {
177
+ return {
178
+ subaccount: mention.subaccount,
179
+ start: mention.start,
180
+ end: mention.end,
181
+ label: mention.label,
182
+ };
183
+ }
@@ -7,6 +7,21 @@ export const MOBILE_EIP712_METHOD_BY_TYPE: Record<
7
7
  MobileSignedInner['type'],
8
8
  string
9
9
  > = {
10
+ register_expo_token_v2: 'mobile:execute_register_expo_token_v2',
11
+ notification_subscriptions: 'mobile:query_notification_subscriptions',
12
+ update_notification_subscriptions:
13
+ 'mobile:execute_update_notification_subscriptions',
14
+ notification_inbox_v2: 'mobile:query_notification_inbox_v2',
15
+ record_product_events: 'mobile:execute_record_product_events',
16
+ create_feed_post: 'mobile:execute_create_feed_post',
17
+ create_feed_comment: 'mobile:execute_create_feed_comment',
18
+ create_feed_post_image_uploads:
19
+ 'mobile:execute_create_feed_post_image_uploads',
20
+ create_feed_comment_image_uploads:
21
+ 'mobile:execute_create_feed_comment_image_uploads',
22
+ create_feed_chart_drawing: 'mobile:execute_create_feed_chart_drawing',
23
+ update_feed_chart_drawing: 'mobile:execute_update_feed_chart_drawing',
24
+ set_feed_like: 'mobile:execute_set_feed_like',
10
25
  set_bio: 'mobile:execute_set_bio',
11
26
  create_avatar_upload: 'mobile:execute_create_avatar_upload',
12
27
  set_avatar: 'mobile:execute_set_avatar',
@@ -10,6 +10,7 @@ import { privateKeyToAccount } from 'viem/accounts';
10
10
  import { ink } from 'viem/chains';
11
11
  import { buildSignedMobileRequest } from './buildSignedMobileRequest';
12
12
  import { canonicalizeMobileInner } from './canonicalize';
13
+ import { MOBILE_EIP712_METHOD_BY_TYPE } from './eip712MethodByType';
13
14
  import { getMobileNonce } from './nonce';
14
15
  import { getMobilePayloadHash } from './payloadHash';
15
16
  import { MobileSignedInner } from './types';
@@ -22,6 +23,20 @@ const FIXED_VERIFYING_ADDR = '0x0000000000000000000000000000000000000001';
22
23
  // Every hash here mirrors the backend's pinned fixtures in mobile/src/api, so a divergence is a
23
24
  // real wire-format break rather than a stale expectation.
24
25
  const PINNED_HASHES = {
26
+ create_feed_post:
27
+ '0x5066f6cf7834957d8ecd713cd87d81da4ddaeffe863b86464fbad1f511713c39',
28
+ create_feed_chart_drawing:
29
+ '0x8ad450257f13a75e7632345676ce2da0f55feda3aa1908ea26e8cccb36ce80e2',
30
+ update_feed_chart_drawing:
31
+ '0xe1b04cd83027aaca1e075a59003f70801164f68552abf47a957721d7d5249cf7',
32
+ create_feed_comment:
33
+ '0x2ceb8d696c5f634db823f2e43274bdf565c36819b305315a61964208effc0476',
34
+ create_feed_comment_reply:
35
+ '0x82c728da0f1c11301dba141a346ac065bd840ab61b69e0c3aea2ceafa77137ae',
36
+ set_feed_like_true:
37
+ '0xbf58b6299a44bfe0a7442cb3920e2f1476eb68d00b137a7d63ade73deb4aded0',
38
+ set_feed_like_false:
39
+ '0xed313b08252b37e58f45dff89afe7e2ab2706a5218db15da7e1d77f2eb43aae5',
25
40
  set_bio: '0x96206cad01c9b00d69412721842ca0866bb1a82ec90f3979b8bcc91dc96a1243',
26
41
  create_avatar_upload:
27
42
  '0xb3811340e751e2799d3a24467fa50aeb221e1ecea5aa6599ab09bd2426162495',
@@ -55,8 +70,122 @@ const FIXTURE_TARGET_SUBACCOUNT =
55
70
  const FIXTURE_SECOND_TARGET_SUBACCOUNT =
56
71
  '0x2222222222222222222222222222222222222222222222222222222222222222' as const;
57
72
 
73
+ /**
74
+ * Feed vectors from nado-mobile-be at 77ccc52988cdc871ba30d6188fd21015b4b1d49f:
75
+ * mobile/tests/fixtures/feed-{publishing,comment,interaction}-signing.json.
76
+ * Pinned hashes are derived from the backend payload_msgpack_hex, not SDK serialization.
77
+ */
78
+ const FEED_SIGNING_FIXTURES = [
79
+ {
80
+ name: 'create_feed_post',
81
+ inner: {
82
+ type: 'create_feed_post',
83
+ client_request_id: '11111111-1111-4111-8111-111111111111',
84
+ position_id: '22222222-2222-4222-8222-222222222222',
85
+ action_id: '33333333-3333-4333-8333-333333333333',
86
+ text: '观点 @Bob',
87
+ mentions: [
88
+ {
89
+ subaccount:
90
+ '0x1111111111111111111111111111111111111111111111111111111111111111',
91
+ start: 3,
92
+ end: 7,
93
+ label: '@Bob',
94
+ },
95
+ ],
96
+ drawing_data_json: '{ "lines": [] }',
97
+ },
98
+ method: 'mobile:execute_create_feed_post',
99
+ },
100
+ {
101
+ name: 'create_feed_chart_drawing',
102
+ inner: {
103
+ type: 'create_feed_chart_drawing',
104
+ item_id: '11111111-1111-4111-8111-111111111111',
105
+ data_json: 'false',
106
+ },
107
+ method: 'mobile:execute_create_feed_chart_drawing',
108
+ },
109
+ {
110
+ name: 'update_feed_chart_drawing',
111
+ inner: {
112
+ type: 'update_feed_chart_drawing',
113
+ item_id: '11111111-1111-4111-8111-111111111111',
114
+ data_json: '0',
115
+ },
116
+ method: 'mobile:execute_update_feed_chart_drawing',
117
+ },
118
+ {
119
+ name: 'create_feed_comment',
120
+ inner: {
121
+ type: 'create_feed_comment',
122
+ client_request_id: '11111111-1111-4111-8111-111111111111',
123
+ root_item_id: '22222222-2222-4222-8222-222222222222',
124
+ text: 'comment',
125
+ mentions: [],
126
+ },
127
+ method: 'mobile:execute_create_feed_comment',
128
+ },
129
+ {
130
+ name: 'create_feed_comment_reply',
131
+ inner: {
132
+ type: 'create_feed_comment',
133
+ client_request_id: '11111111-1111-4111-8111-111111111111',
134
+ root_item_id: '22222222-2222-4222-8222-222222222222',
135
+ reply_to_item_id: '33333333-3333-4333-8333-333333333333',
136
+ text: '评论 @Alice',
137
+ mentions: [
138
+ {
139
+ subaccount:
140
+ '0x1111111111111111111111111111111111111111111111111111111111111111',
141
+ start: 3,
142
+ end: 9,
143
+ label: '@Alice',
144
+ },
145
+ ],
146
+ attached_position_id: '44444444-4444-4444-8444-444444444444',
147
+ },
148
+ method: 'mobile:execute_create_feed_comment',
149
+ },
150
+ {
151
+ name: 'set_feed_like_true',
152
+ inner: {
153
+ type: 'set_feed_like',
154
+ item_id: '11111111-1111-4111-8111-111111111111',
155
+ is_liked: true,
156
+ },
157
+ method: 'mobile:execute_set_feed_like',
158
+ },
159
+ {
160
+ name: 'set_feed_like_false',
161
+ inner: {
162
+ type: 'set_feed_like',
163
+ item_id: '11111111-1111-4111-8111-111111111111',
164
+ is_liked: false,
165
+ },
166
+ method: 'mobile:execute_set_feed_like',
167
+ },
168
+ ] satisfies {
169
+ name: keyof typeof PINNED_HASHES;
170
+ inner: MobileSignedInner;
171
+ method: string;
172
+ }[];
173
+
58
174
  describe('[mobile-client]: signing (offline)', () => {
59
175
  describe('pinned payload hashes', () => {
176
+ it.each(FEED_SIGNING_FIXTURES)(
177
+ '$name matches the backend signing vector',
178
+ ({ name, inner, method }) => {
179
+ const reordered = Object.fromEntries(
180
+ Object.entries(inner).reverse(),
181
+ ) as MobileSignedInner;
182
+ expect(getMobilePayloadHash(canonicalizeMobileInner(reordered))).toBe(
183
+ PINNED_HASHES[name],
184
+ );
185
+ expect(MOBILE_EIP712_METHOD_BY_TYPE[inner.type]).toBe(method);
186
+ },
187
+ );
188
+
60
189
  it('set_bio', () => {
61
190
  const inner: MobileSignedInner = {
62
191
  type: 'set_bio',
@@ -291,3 +420,236 @@ describe('[mobile-client]: signing (offline)', () => {
291
420
  expect(recoveredAddress.toLowerCase()).toBe(account.address.toLowerCase());
292
421
  });
293
422
  });
423
+
424
+ describe('[mobile-client]: social content signing', () => {
425
+ const requestId = 'AAAAAAAA-AAAA-4AAA-8AAA-AAAAAAAAAAAA';
426
+ const positionId = 'BBBBBBBB-BBBB-4BBB-8BBB-BBBBBBBBBBBB';
427
+ const itemId = 'CCCCCCCC-CCCC-4CCC-8CCC-CCCCCCCCCCCC';
428
+ const mention = {
429
+ label: '@Alice',
430
+ end: 8,
431
+ start: 2,
432
+ subaccount: FIXTURE_TARGET_SUBACCOUNT,
433
+ };
434
+ const text = '🚀 @Alice ';
435
+ const drawing = ' { "lines" : [], "enabled": false } ';
436
+
437
+ it('canonicalizes every post field and nested mention without changing content', () => {
438
+ const imageIds = ['88888888-8888-4888-8888-888888888888'];
439
+ const input: MobileSignedInner = {
440
+ image_ids: imageIds,
441
+ drawing_data_json: drawing,
442
+ mentions: [mention],
443
+ text,
444
+ action_id: itemId,
445
+ position_id: positionId,
446
+ client_request_id: requestId,
447
+ type: 'create_feed_post',
448
+ };
449
+ const result = canonicalizeMobileInner(input);
450
+ expect(Object.keys(result)).toEqual([
451
+ 'type',
452
+ 'client_request_id',
453
+ 'position_id',
454
+ 'action_id',
455
+ 'text',
456
+ 'mentions',
457
+ 'drawing_data_json',
458
+ 'image_ids',
459
+ ]);
460
+ if (result.type !== 'create_feed_post') throw new Error('Expected post');
461
+ expect(Object.keys(result.mentions[0])).toEqual([
462
+ 'subaccount',
463
+ 'start',
464
+ 'end',
465
+ 'label',
466
+ ]);
467
+ expect(result.client_request_id).toBe(requestId);
468
+ expect(result.action_id).toBe(itemId);
469
+ expect(result.position_id).toBe(positionId);
470
+ expect(result.text).toBe(text);
471
+ expect(result.drawing_data_json).toBe(drawing);
472
+ expect(result.image_ids).toEqual(imageIds);
473
+ expect(result.image_ids).not.toBe(imageIds);
474
+ expect(result.mentions[0]).toEqual({
475
+ subaccount: FIXTURE_TARGET_SUBACCOUNT,
476
+ start: 2,
477
+ end: 8,
478
+ label: '@Alice',
479
+ });
480
+ expect(input.position_id).toBe(positionId);
481
+ expect(Object.keys(mention)).toEqual([
482
+ 'label',
483
+ 'end',
484
+ 'start',
485
+ 'subaccount',
486
+ ]);
487
+ });
488
+
489
+ it('omits absent optional fields but retains empty text and mention arrays', () => {
490
+ const post = canonicalizeMobileInner({
491
+ type: 'create_feed_post',
492
+ client_request_id: requestId,
493
+ position_id: positionId,
494
+ action_id: undefined,
495
+ text: '',
496
+ mentions: [],
497
+ drawing_data_json: 'false',
498
+ });
499
+ expect(Object.keys(post)).toEqual([
500
+ 'type',
501
+ 'client_request_id',
502
+ 'position_id',
503
+ 'text',
504
+ 'mentions',
505
+ 'drawing_data_json',
506
+ ]);
507
+ const comment = canonicalizeMobileInner({
508
+ type: 'create_feed_comment',
509
+ client_request_id: requestId,
510
+ root_item_id: itemId,
511
+ reply_to_item_id: undefined,
512
+ text,
513
+ mentions: [],
514
+ attached_position_id: undefined,
515
+ });
516
+ expect(Object.keys(comment)).toEqual([
517
+ 'type',
518
+ 'client_request_id',
519
+ 'root_item_id',
520
+ 'text',
521
+ 'mentions',
522
+ ]);
523
+ expect(getMobilePayloadHash(comment)).not.toBe(
524
+ getMobilePayloadHash({
525
+ ...comment,
526
+ reply_to_item_id: null,
527
+ } as unknown as MobileSignedInner),
528
+ );
529
+ });
530
+
531
+ it('canonicalizes comment optionals in backend order and preserves whitespace', () => {
532
+ const comment = canonicalizeMobileInner({
533
+ attached_position_id: positionId,
534
+ mentions: [mention],
535
+ text,
536
+ reply_to_item_id: itemId,
537
+ root_item_id: requestId,
538
+ client_request_id: requestId,
539
+ type: 'create_feed_comment',
540
+ });
541
+ expect(Object.keys(comment)).toEqual([
542
+ 'type',
543
+ 'client_request_id',
544
+ 'root_item_id',
545
+ 'reply_to_item_id',
546
+ 'text',
547
+ 'mentions',
548
+ 'attached_position_id',
549
+ ]);
550
+ if (comment.type !== 'create_feed_comment')
551
+ throw new Error('Expected comment');
552
+ expect(comment.attached_position_id).toBe(positionId);
553
+ expect(comment.text).toBe(text);
554
+ expect(Object.keys(comment.mentions[0])).toEqual([
555
+ 'subaccount',
556
+ 'start',
557
+ 'end',
558
+ 'label',
559
+ ]);
560
+ });
561
+
562
+ it('does not reformat JSON before hashing or normalize falsy drawing values', () => {
563
+ const inner: MobileSignedInner = {
564
+ type: 'update_feed_chart_drawing',
565
+ item_id: itemId,
566
+ data_json: drawing,
567
+ };
568
+ const canonical = canonicalizeMobileInner(inner);
569
+ expect(Object.keys(canonical)).toEqual(['type', 'item_id', 'data_json']);
570
+ expect(getMobilePayloadHash(canonical)).not.toBe(
571
+ getMobilePayloadHash(
572
+ canonicalizeMobileInner({
573
+ ...inner,
574
+ data_json: JSON.stringify(JSON.parse(drawing)),
575
+ }),
576
+ ),
577
+ );
578
+ for (const dataJson of ['false', '0']) {
579
+ expect(
580
+ canonicalizeMobileInner({ ...inner, data_json: dataJson }),
581
+ ).toEqual({
582
+ type: inner.type,
583
+ item_id: itemId,
584
+ data_json: dataJson,
585
+ });
586
+ }
587
+ });
588
+
589
+ const payloads: MobileSignedInner[] = [
590
+ {
591
+ type: 'create_feed_post_image_uploads',
592
+ content_hash:
593
+ '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
594
+ },
595
+ {
596
+ type: 'create_feed_comment_image_uploads',
597
+ content_hash:
598
+ '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
599
+ },
600
+ {
601
+ type: 'create_feed_post',
602
+ client_request_id: requestId,
603
+ position_id: positionId,
604
+ text,
605
+ mentions: [mention],
606
+ },
607
+ {
608
+ type: 'create_feed_comment',
609
+ client_request_id: requestId,
610
+ root_item_id: itemId,
611
+ text,
612
+ mentions: [],
613
+ },
614
+ { type: 'create_feed_chart_drawing', item_id: itemId, data_json: 'false' },
615
+ { type: 'update_feed_chart_drawing', item_id: itemId, data_json: '0' },
616
+ { type: 'set_feed_like', item_id: itemId, is_liked: false },
617
+ ];
618
+ it.each(payloads)(
619
+ 'recovers the signer for $type using its execute method domain',
620
+ async (inner) => {
621
+ const account = privateKeyToAccount(FIXED_PRIVATE_KEY);
622
+ const walletClient = createWalletClient({
623
+ account,
624
+ chain: ink,
625
+ transport: http(),
626
+ });
627
+ const nonce = 30_000_000_000_000n;
628
+ const signed = await buildSignedMobileRequest({
629
+ walletClient,
630
+ subaccountOwner: account.address,
631
+ subaccountName: 'default',
632
+ chainId: ink.id,
633
+ verifyingAddr: FIXED_VERIFYING_ADDR,
634
+ nonce,
635
+ inner,
636
+ });
637
+ const canonical = canonicalizeMobileInner(inner);
638
+ const recovered = await recoverTypedDataAddress({
639
+ domain: getNadoEIP712Domain(FIXED_VERIFYING_ADDR, ink.id),
640
+ types: getNadoEIP712Types('nado_authentication'),
641
+ primaryType: getNadoEIP712PrimaryType('nado_authentication'),
642
+ message: {
643
+ method: 'mobile:execute_' + inner.type,
644
+ sender: signed.sender,
645
+ payloadHash: getMobilePayloadHash(canonical),
646
+ nonce,
647
+ },
648
+ signature: signed.signature,
649
+ });
650
+ expect(recovered.toLowerCase()).toBe(account.address.toLowerCase());
651
+ expect(signed).toMatchObject({ ...canonical });
652
+ expect(signed).not.toHaveProperty('payload');
653
+ },
654
+ );
655
+ });
@@ -2,9 +2,15 @@ import { WalletClientWithAccount } from '@nadohq/shared';
2
2
  import { Hex } from 'viem';
3
3
  import { MobileSignedRequestParams } from '../types/clientTypes';
4
4
  import {
5
- MobileNotificationPlatform,
6
5
  MobileServerAvatarSelection,
6
+ MobileServerProductEvent,
7
+ MobileSocialFeedMention,
7
8
  } from '../types/serverModelTypes';
9
+ import {
10
+ MobileServerNotificationSubscriptionsUpdate,
11
+ MobileServerRegisterExpoTokenParams,
12
+ MobileServerRegisterExpoTokenV2Params,
13
+ } from '../types/serverRequestTypes';
8
14
 
9
15
  /**
10
16
  * Unsigned inner payloads that can be authenticated against the mobile service API.
@@ -21,6 +27,104 @@ import {
21
27
  * lowercases; unlike bytes32, string fields such as `display_name` are hashed as-is and keep their casing.
22
28
  */
23
29
  export type MobileSignedInner =
30
+ | ({
31
+ /** Capability-aware device registration. */
32
+ type: 'register_expo_token_v2';
33
+ } & MobileServerRegisterExpoTokenV2Params)
34
+ | {
35
+ /** Signed query for wallet markets and Identity bells. */
36
+ type: 'notification_subscriptions';
37
+ }
38
+ | ({
39
+ /** One revision-guarded subscription operation. */
40
+ type: 'update_notification_subscriptions';
41
+ } & MobileServerNotificationSubscriptionsUpdate)
42
+ | {
43
+ /** Signed query for the wallet's private notification page. */
44
+ type: 'notification_inbox_v2';
45
+ /** Previous page cursor, or nil for the first page. */
46
+ cursor: string | null;
47
+ /** Page size, or nil for the server default. */
48
+ limit: number | null;
49
+ }
50
+ | {
51
+ /** Atomic product analytics batch. */
52
+ type: 'record_product_events';
53
+ /** Events in caller order. */
54
+ events: MobileServerProductEvent[];
55
+ }
56
+ | {
57
+ /** Atomic post publication. */
58
+ type: 'create_feed_post';
59
+ /** Author-scoped idempotency UUID. */
60
+ client_request_id: string;
61
+ /** Owned position UUID. */
62
+ position_id: string;
63
+ /** Optional trusted action UUID, omitted rather than encoded as nil. */
64
+ action_id?: string;
65
+ /** Unmodified original text. */
66
+ text: string;
67
+ /** Ordered mentions into the original text. */
68
+ mentions: MobileSocialFeedMention[];
69
+ /** Original JSON text; omitted when absent. */
70
+ drawing_data_json?: string;
71
+ /** Ordered ready image UUIDs; omitted when absent. */
72
+ image_ids?: string[];
73
+ }
74
+ | {
75
+ /** Atomic comment publication. */
76
+ type: 'create_feed_comment';
77
+ /** Author-scoped idempotency UUID. */
78
+ client_request_id: string;
79
+ /** Root UUID. */
80
+ root_item_id: string;
81
+ /** Optional same-thread reply UUID. */
82
+ reply_to_item_id?: string;
83
+ /** Unmodified original text. */
84
+ text: string;
85
+ /** Ordered mentions. */
86
+ mentions: MobileSocialFeedMention[];
87
+ /** Optional owned position UUID. */
88
+ attached_position_id?: string;
89
+ /** Ordered ready image UUIDs; omitted when absent. */
90
+ image_ids?: string[];
91
+ }
92
+ | {
93
+ /** Direct multipart post-image upload; content_hash is Keccak256 of the raw file bytes. */
94
+ type: 'create_feed_post_image_uploads';
95
+ /** `0x`-prefixed lowercase Keccak256 of the original file bytes. */
96
+ content_hash: Hex;
97
+ }
98
+ | {
99
+ /** Direct multipart comment-image upload; content_hash is Keccak256 of the raw file bytes. */
100
+ type: 'create_feed_comment_image_uploads';
101
+ /** `0x`-prefixed lowercase Keccak256 of the original file bytes. */
102
+ content_hash: Hex;
103
+ }
104
+ | {
105
+ /** First attachment. */
106
+ type: 'create_feed_chart_drawing';
107
+ /** Parent UUID. */
108
+ item_id: string;
109
+ /** Original JSON text, not parsed or reformatted before hashing. */
110
+ data_json: string;
111
+ }
112
+ | {
113
+ /** Full replacement of an existing drawing. */
114
+ type: 'update_feed_chart_drawing';
115
+ /** Parent UUID. */
116
+ item_id: string;
117
+ /** Original JSON text. */
118
+ data_json: string;
119
+ }
120
+ | {
121
+ /** Idempotent relationship mutation. */
122
+ type: 'set_feed_like';
123
+ /** Item UUID. */
124
+ item_id: string;
125
+ /** Desired relationship state. */
126
+ is_liked: boolean;
127
+ }
24
128
  | { type: 'set_bio'; bio: string }
25
129
  | { type: 'create_avatar_upload'; content_length: number }
26
130
  | {
@@ -29,15 +133,10 @@ export type MobileSignedInner =
29
133
  }
30
134
  | { type: 'set_username'; display_name: string }
31
135
  | { type: 'set_private_mode'; private_mode: boolean }
32
- | {
136
+ | ({
137
+ /** Legacy device registration without a capability declaration. */
33
138
  type: 'register_expo_token';
34
- expo_token: string;
35
- platform: MobileNotificationPlatform;
36
- // Optional on the wire, but always present (null when unset) so the msgpack payload hash matches the
37
- // backend, which serializes `Option::None` as nil under the same key.
38
- locale: string | null;
39
- app_version: string | null;
40
- }
139
+ } & MobileServerRegisterExpoTokenParams)
41
140
  | {
42
141
  type: 'set_follow';
43
142
  subaccount: Hex;
@@ -85,8 +184,4 @@ export type MobileSignedRequest<
85
184
  export interface BuildSignedMobileRequestParams extends MobileSignedRequestParams {
86
185
  walletClient: WalletClientWithAccount;
87
186
  inner: MobileSignedInner;
88
- /**
89
- * Overrides the generated nonce; mainly useful for tests that pin the signed payload.
90
- */
91
- nonce?: bigint;
92
187
  }