@nadohq/mobile-client 0.36.0 → 0.37.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.
- package/README.md +19 -6
- package/dist/{MobileClient-D5TNTnLx.cjs → MobileClient-C9S_o_eC.cjs} +82 -12
- package/dist/MobileClient-C9S_o_eC.cjs.map +1 -0
- package/dist/MobileClient.cjs +1 -1
- package/dist/MobileClient.d.cts +64 -16
- package/dist/MobileClient.d.cts.map +1 -1
- package/dist/MobileClient.d.ts +64 -16
- package/dist/MobileClient.d.ts.map +1 -1
- package/dist/MobileClient.js +82 -12
- package/dist/MobileClient.js.map +1 -1
- package/dist/{MobileServerFailureError-CPFjwUBx.d.cts → MobileServerFailureError-CVK3aLOi.d.cts} +3 -3
- package/dist/{MobileServerFailureError-CPFjwUBx.d.cts.map → MobileServerFailureError-CVK3aLOi.d.cts.map} +1 -1
- package/dist/{MobileServerFailureError-Terv2Wzy.d.ts → MobileServerFailureError-D-76n3pS.d.ts} +3 -3
- package/dist/{MobileServerFailureError-Terv2Wzy.d.ts.map → MobileServerFailureError-D-76n3pS.d.ts.map} +1 -1
- package/dist/{buildSignedMobileRequest-BScnCGPF.d.ts → buildSignedMobileRequest-42meR4yV.d.ts} +2 -2
- package/dist/{buildSignedMobileRequest-BScnCGPF.d.ts.map → buildSignedMobileRequest-42meR4yV.d.ts.map} +1 -1
- package/dist/{buildSignedMobileRequest-CXfnhc-4.d.cts → buildSignedMobileRequest-BDxji7Vy.d.cts} +2 -2
- package/dist/{buildSignedMobileRequest-CXfnhc-4.d.cts.map → buildSignedMobileRequest-BDxji7Vy.d.cts.map} +1 -1
- package/dist/{canonicalize-CQ-pMYIt.d.ts → canonicalize-BebHhHcF.d.ts} +2 -2
- package/dist/canonicalize-BebHhHcF.d.ts.map +1 -0
- package/dist/{canonicalize-hzhboB-C.d.cts → canonicalize-CIDJLLgg.d.cts} +2 -2
- package/dist/canonicalize-CIDJLLgg.d.cts.map +1 -0
- package/dist/{clientTypes-BwakovWk.d.cts → clientTypes-2I3x4TTt.d.ts} +142 -14
- package/dist/clientTypes-2I3x4TTt.d.ts.map +1 -0
- package/dist/{clientTypes-D-o1_2Yc.d.ts → clientTypes-tLld8E6J.d.cts} +142 -14
- package/dist/clientTypes-tLld8E6J.d.cts.map +1 -0
- package/dist/dataMappers.cjs +65 -6
- package/dist/dataMappers.cjs.map +1 -1
- package/dist/dataMappers.d.cts +21 -5
- package/dist/dataMappers.d.cts.map +1 -1
- package/dist/dataMappers.d.ts +21 -5
- package/dist/dataMappers.d.ts.map +1 -1
- package/dist/dataMappers.js +62 -7
- package/dist/dataMappers.js.map +1 -1
- package/dist/{eip712MethodByType-3aiJkrHj.d.cts → eip712MethodByType-2-vyN5SU.d.cts} +2 -2
- package/dist/eip712MethodByType-2-vyN5SU.d.cts.map +1 -0
- package/dist/{eip712MethodByType-zNvZukU7.d.ts → eip712MethodByType-CAVpvZoS.d.ts} +2 -2
- package/dist/eip712MethodByType-CAVpvZoS.d.ts.map +1 -0
- package/dist/follow-Bh3UmXMc.d.cts +23 -0
- package/dist/follow-Bh3UmXMc.d.cts.map +1 -0
- package/dist/follow-DjkQm1vR.d.ts +23 -0
- package/dist/follow-DjkQm1vR.d.ts.map +1 -0
- package/dist/index.cjs +6 -1
- package/dist/index.d.cts +17 -16
- package/dist/index.d.ts +17 -16
- package/dist/index.js +2 -1
- package/dist/{mobileErrorCodes-CuQ5lyBV.d.ts → mobileErrorCodes-B3eR6t4b.d.ts} +8 -2
- package/dist/mobileErrorCodes-B3eR6t4b.d.ts.map +1 -0
- package/dist/{mobileErrorCodes-D_SyBm0k.d.cts → mobileErrorCodes-DkzBW4v3.d.cts} +8 -2
- package/dist/mobileErrorCodes-DkzBW4v3.d.cts.map +1 -0
- package/dist/{patterns-B5gi7OfV.d.ts → patterns-C-aUAnKl.d.ts} +1 -1
- package/dist/patterns-C-aUAnKl.d.ts.map +1 -0
- package/dist/{patterns-Db6fgz8i.d.cts → patterns-DS8P5LH0.d.cts} +1 -1
- package/dist/patterns-DS8P5LH0.d.cts.map +1 -0
- package/dist/{payloadHash-DVkEt-Rg.d.cts → payloadHash-BVPYKdk4.d.cts} +2 -2
- package/dist/payloadHash-BVPYKdk4.d.cts.map +1 -0
- package/dist/{payloadHash-BxL26uwq.d.ts → payloadHash-DMqE5aMo.d.ts} +2 -2
- package/dist/payloadHash-DMqE5aMo.d.ts.map +1 -0
- package/dist/{serverBaseTypes-C8KjE9Fm.d.cts → serverBaseTypes-BybrVE6L.d.cts} +2 -2
- package/dist/{serverBaseTypes-C8KjE9Fm.d.cts.map → serverBaseTypes-BybrVE6L.d.cts.map} +1 -1
- package/dist/{serverBaseTypes-B_KHFDab.d.ts → serverBaseTypes-DPB1vEvv.d.ts} +2 -2
- package/dist/{serverBaseTypes-B_KHFDab.d.ts.map → serverBaseTypes-DPB1vEvv.d.ts.map} +1 -1
- package/dist/serverExecuteTypes-DoR0InCd.d.cts +50 -0
- package/dist/serverExecuteTypes-DoR0InCd.d.cts.map +1 -0
- package/dist/serverExecuteTypes-ih4Xbr3J.d.ts +50 -0
- package/dist/serverExecuteTypes-ih4Xbr3J.d.ts.map +1 -0
- package/dist/{serverModelTypes-_cX99seo.d.ts → serverModelTypes-DV-eARP8.d.ts} +54 -10
- package/dist/serverModelTypes-DV-eARP8.d.ts.map +1 -0
- package/dist/{serverModelTypes-CwgibK-l.d.cts → serverModelTypes-KSm0CAcF.d.cts} +54 -10
- package/dist/serverModelTypes-KSm0CAcF.d.cts.map +1 -0
- package/dist/{serverQueryTypes-NIrPm2Sl.d.ts → serverQueryTypes-B9OOpiJB.d.cts} +32 -9
- package/dist/serverQueryTypes-B9OOpiJB.d.cts.map +1 -0
- package/dist/{serverQueryTypes-DwE1iJ9f.d.cts → serverQueryTypes-LygPrZn7.d.ts} +32 -9
- package/dist/serverQueryTypes-LygPrZn7.d.ts.map +1 -0
- package/dist/serverRequestTypes-BDXIPjtw.d.ts +101 -0
- package/dist/serverRequestTypes-BDXIPjtw.d.ts.map +1 -0
- package/dist/serverRequestTypes-q4xTJHk0.d.cts +101 -0
- package/dist/serverRequestTypes-q4xTJHk0.d.cts.map +1 -0
- package/dist/{serverResponseGuards-Cep5s7j5.d.ts → serverResponseGuards-BcdkKV30.d.ts} +2 -2
- package/dist/{serverResponseGuards-Cep5s7j5.d.ts.map → serverResponseGuards-BcdkKV30.d.ts.map} +1 -1
- package/dist/{serverResponseGuards-Dxt9_aGh.d.cts → serverResponseGuards-k6xK1bZ1.d.cts} +2 -2
- package/dist/{serverResponseGuards-Dxt9_aGh.d.cts.map → serverResponseGuards-k6xK1bZ1.d.cts.map} +1 -1
- package/dist/signing/buildSignedMobileRequest.cjs +1 -1
- package/dist/signing/buildSignedMobileRequest.d.cts +1 -1
- package/dist/signing/buildSignedMobileRequest.d.ts +1 -1
- package/dist/signing/canonicalize.cjs +17 -0
- package/dist/signing/canonicalize.cjs.map +1 -1
- package/dist/signing/canonicalize.d.cts +1 -1
- package/dist/signing/canonicalize.d.ts +1 -1
- package/dist/signing/canonicalize.js +17 -0
- package/dist/signing/canonicalize.js.map +1 -1
- package/dist/signing/eip712MethodByType.cjs +4 -1
- package/dist/signing/eip712MethodByType.cjs.map +1 -1
- package/dist/signing/eip712MethodByType.d.cts +1 -1
- package/dist/signing/eip712MethodByType.d.ts +1 -1
- package/dist/signing/eip712MethodByType.js +4 -1
- package/dist/signing/eip712MethodByType.js.map +1 -1
- package/dist/signing/index.d.cts +6 -6
- package/dist/signing/index.d.ts +6 -6
- package/dist/signing/payloadHash.cjs +1 -1
- package/dist/signing/payloadHash.d.cts +1 -1
- package/dist/signing/payloadHash.d.ts +1 -1
- package/dist/signing/types.d.cts +2 -2
- package/dist/signing/types.d.ts +2 -2
- package/dist/types/MobileServerFailureError.cjs +1 -1
- package/dist/types/MobileServerFailureError.d.cts +1 -1
- package/dist/types/MobileServerFailureError.d.ts +1 -1
- package/dist/types/clientTypes.d.cts +2 -2
- package/dist/types/clientTypes.d.ts +2 -2
- package/dist/types/index.d.cts +9 -9
- package/dist/types/index.d.ts +9 -9
- package/dist/types/mobileErrorCodes.cjs +13 -1
- package/dist/types/mobileErrorCodes.cjs.map +1 -1
- package/dist/types/mobileErrorCodes.d.cts +1 -1
- package/dist/types/mobileErrorCodes.d.ts +1 -1
- package/dist/types/mobileErrorCodes.js +12 -0
- package/dist/types/mobileErrorCodes.js.map +1 -1
- package/dist/types/serverBaseTypes.d.cts +1 -1
- package/dist/types/serverBaseTypes.d.ts +1 -1
- package/dist/types/serverExecuteTypes.d.cts +2 -2
- package/dist/types/serverExecuteTypes.d.ts +2 -2
- package/dist/types/serverModelTypes.cjs.map +1 -1
- package/dist/types/serverModelTypes.d.cts +2 -2
- package/dist/types/serverModelTypes.d.ts +2 -2
- package/dist/types/serverModelTypes.js.map +1 -1
- package/dist/types/serverQueryTypes.d.cts +2 -2
- package/dist/types/serverQueryTypes.d.ts +2 -2
- package/dist/types/serverRequestTypes.d.cts +2 -2
- package/dist/types/serverRequestTypes.d.ts +2 -2
- package/dist/{types-47IPsDyA.d.cts → types-B8lDIhBP.d.ts} +30 -7
- package/dist/types-B8lDIhBP.d.ts.map +1 -0
- package/dist/{types-D5kFJzEX.d.ts → types-CClB00EN.d.cts} +30 -7
- package/dist/types-CClB00EN.d.cts.map +1 -0
- package/dist/utils/follow.cjs +28 -0
- package/dist/utils/follow.cjs.map +1 -0
- package/dist/utils/follow.d.cts +2 -0
- package/dist/utils/follow.d.ts +2 -0
- package/dist/utils/follow.js +24 -0
- package/dist/utils/follow.js.map +1 -0
- package/dist/utils/index.cjs +5 -0
- package/dist/utils/index.d.cts +4 -3
- package/dist/utils/index.d.ts +4 -3
- package/dist/utils/index.js +2 -1
- package/dist/utils/patterns.d.cts +1 -1
- package/dist/utils/patterns.d.ts +1 -1
- package/dist/utils/serverResponseGuards.d.cts +1 -1
- package/dist/utils/serverResponseGuards.d.ts +1 -1
- package/package.json +3 -3
- package/src/MobileClient.ts +138 -21
- package/src/dataMappers.ts +89 -5
- package/src/signing/canonicalize.ts +20 -0
- package/src/signing/eip712MethodByType.ts +3 -0
- package/src/signing/signing.test.ts +58 -4
- package/src/signing/types.ts +34 -1
- package/src/types/clientTypes.ts +148 -11
- package/src/types/mobileErrorCodes.ts +12 -0
- package/src/types/serverExecuteTypes.ts +40 -2
- package/src/types/serverModelTypes.ts +55 -8
- package/src/types/serverQueryTypes.ts +35 -4
- package/src/types/serverRequestTypes.ts +41 -6
- package/src/utils/follow.ts +22 -0
- package/src/utils/index.ts +1 -0
- package/dist/MobileClient-D5TNTnLx.cjs.map +0 -1
- package/dist/canonicalize-CQ-pMYIt.d.ts.map +0 -1
- package/dist/canonicalize-hzhboB-C.d.cts.map +0 -1
- package/dist/clientTypes-BwakovWk.d.cts.map +0 -1
- package/dist/clientTypes-D-o1_2Yc.d.ts.map +0 -1
- package/dist/eip712MethodByType-3aiJkrHj.d.cts.map +0 -1
- package/dist/eip712MethodByType-zNvZukU7.d.ts.map +0 -1
- package/dist/mobileErrorCodes-CuQ5lyBV.d.ts.map +0 -1
- package/dist/mobileErrorCodes-D_SyBm0k.d.cts.map +0 -1
- package/dist/patterns-B5gi7OfV.d.ts.map +0 -1
- package/dist/patterns-Db6fgz8i.d.cts.map +0 -1
- package/dist/payloadHash-BxL26uwq.d.ts.map +0 -1
- package/dist/payloadHash-DVkEt-Rg.d.cts.map +0 -1
- package/dist/serverExecuteTypes-Cb5v7y0A.d.cts +0 -19
- package/dist/serverExecuteTypes-Cb5v7y0A.d.cts.map +0 -1
- package/dist/serverExecuteTypes-GFjZj_DC.d.ts +0 -19
- package/dist/serverExecuteTypes-GFjZj_DC.d.ts.map +0 -1
- package/dist/serverModelTypes-CwgibK-l.d.cts.map +0 -1
- package/dist/serverModelTypes-_cX99seo.d.ts.map +0 -1
- package/dist/serverQueryTypes-DwE1iJ9f.d.cts.map +0 -1
- package/dist/serverQueryTypes-NIrPm2Sl.d.ts.map +0 -1
- package/dist/serverRequestTypes-516zXnQ6.d.ts +0 -72
- package/dist/serverRequestTypes-516zXnQ6.d.ts.map +0 -1
- package/dist/serverRequestTypes-GAtVYIda.d.cts +0 -72
- package/dist/serverRequestTypes-GAtVYIda.d.cts.map +0 -1
- package/dist/types-47IPsDyA.d.cts.map +0 -1
- package/dist/types-D5kFJzEX.d.ts.map +0 -1
|
@@ -19,8 +19,8 @@ const FIXED_PRIVATE_KEY =
|
|
|
19
19
|
'0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b786907' as const;
|
|
20
20
|
const FIXED_VERIFYING_ADDR = '0x0000000000000000000000000000000000000001';
|
|
21
21
|
|
|
22
|
-
//
|
|
23
|
-
//
|
|
22
|
+
// Every hash here mirrors the backend's pinned fixtures in mobile/src/api/types.rs, so a divergence is a
|
|
23
|
+
// real wire-format break rather than a stale expectation.
|
|
24
24
|
const PINNED_HASHES = {
|
|
25
25
|
set_username:
|
|
26
26
|
'0xb4a55d9eb0be4e5c9457761c0c34c75cf39f7c07bd170700d8dd5e9c7de4a659',
|
|
@@ -28,8 +28,20 @@ const PINNED_HASHES = {
|
|
|
28
28
|
'0x4d12a06234d751e6ddcc01d8f70836bb5b7e207e573641d1efb5aaf2b0f30d10',
|
|
29
29
|
register_expo_token:
|
|
30
30
|
'0x1b9471afc9bde66f9bffd576d3326420e1bade4e16afa3a73ddc65f27155611c',
|
|
31
|
+
set_follow_true:
|
|
32
|
+
'0x1ff8529b7c1a0111313eed98536e591555a3bcab51133776ce30c6ef32b559b3',
|
|
33
|
+
set_follow_false:
|
|
34
|
+
'0xec9e9e8f32ac4fcf3015f8b7f20c338623ff85db0488703c7fb688519a46e2e5',
|
|
35
|
+
followers:
|
|
36
|
+
'0x44f3cd2c991196cf949a25cebe0e16a4e5e8a14846d35751f3931de3c8ffcabd',
|
|
37
|
+
following:
|
|
38
|
+
'0xaddabad62abf1919dcd4250eee20359970cbfb59f4c81536551462f8918e5ddd',
|
|
31
39
|
} as const;
|
|
32
40
|
|
|
41
|
+
// Every follow fixture on the backend targets this subaccount.
|
|
42
|
+
const FIXTURE_TARGET_SUBACCOUNT =
|
|
43
|
+
'0x1111111111111111111111111111111111111111111111111111111111111111' as const;
|
|
44
|
+
|
|
33
45
|
describe('[mobile-client]: signing (offline)', () => {
|
|
34
46
|
describe('pinned payload hashes', () => {
|
|
35
47
|
it('set_username', () => {
|
|
@@ -61,6 +73,48 @@ describe('[mobile-client]: signing (offline)', () => {
|
|
|
61
73
|
const hash = getMobilePayloadHash(canonicalizeMobileInner(inner));
|
|
62
74
|
expect(hash).toBe(PINNED_HASHES.register_expo_token);
|
|
63
75
|
});
|
|
76
|
+
|
|
77
|
+
it('set_follow (follow)', () => {
|
|
78
|
+
const inner: MobileSignedInner = {
|
|
79
|
+
type: 'set_follow',
|
|
80
|
+
subaccount: FIXTURE_TARGET_SUBACCOUNT,
|
|
81
|
+
is_following: true,
|
|
82
|
+
};
|
|
83
|
+
const hash = getMobilePayloadHash(canonicalizeMobileInner(inner));
|
|
84
|
+
expect(hash).toBe(PINNED_HASHES.set_follow_true);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('set_follow (unfollow)', () => {
|
|
88
|
+
const inner: MobileSignedInner = {
|
|
89
|
+
type: 'set_follow',
|
|
90
|
+
subaccount: FIXTURE_TARGET_SUBACCOUNT,
|
|
91
|
+
is_following: false,
|
|
92
|
+
};
|
|
93
|
+
const hash = getMobilePayloadHash(canonicalizeMobileInner(inner));
|
|
94
|
+
expect(hash).toBe(PINNED_HASHES.set_follow_false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('followers', () => {
|
|
98
|
+
const inner: MobileSignedInner = {
|
|
99
|
+
type: 'followers',
|
|
100
|
+
subaccount: FIXTURE_TARGET_SUBACCOUNT,
|
|
101
|
+
cursor: null,
|
|
102
|
+
limit: 50,
|
|
103
|
+
};
|
|
104
|
+
const hash = getMobilePayloadHash(canonicalizeMobileInner(inner));
|
|
105
|
+
expect(hash).toBe(PINNED_HASHES.followers);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('following', () => {
|
|
109
|
+
const inner: MobileSignedInner = {
|
|
110
|
+
type: 'following',
|
|
111
|
+
subaccount: FIXTURE_TARGET_SUBACCOUNT,
|
|
112
|
+
cursor: null,
|
|
113
|
+
limit: 50,
|
|
114
|
+
};
|
|
115
|
+
const hash = getMobilePayloadHash(canonicalizeMobileInner(inner));
|
|
116
|
+
expect(hash).toBe(PINNED_HASHES.following);
|
|
117
|
+
});
|
|
64
118
|
});
|
|
65
119
|
|
|
66
120
|
it('canonicalizes msgpack key order regardless of input key order', () => {
|
|
@@ -76,8 +130,8 @@ describe('[mobile-client]: signing (offline)', () => {
|
|
|
76
130
|
});
|
|
77
131
|
|
|
78
132
|
it('canonicalizes multi-field key order regardless of input key order', () => {
|
|
79
|
-
// register_expo_token
|
|
80
|
-
//
|
|
133
|
+
// register_expo_token has the most fields of any signed payload, so it is where a caller is most likely
|
|
134
|
+
// to get the order wrong; canonicalizeMobileInner must rebuild it in the backend's declaration order.
|
|
81
135
|
const outOfOrderInner = {
|
|
82
136
|
app_version: '1.2.3',
|
|
83
137
|
locale: 'en-GB',
|
package/src/signing/types.ts
CHANGED
|
@@ -11,6 +11,11 @@ import { MobileNotificationPlatform } from '../types/serverModelTypes';
|
|
|
11
11
|
* declares its structs — `type` first, then the remaining fields — or the hash won't match and signature
|
|
12
12
|
* verification fails. This type only describes the shape; the authoritative runtime key order is enforced by
|
|
13
13
|
* {@link canonicalizeMobileInner}, which is the source of truth. Keep the field order here in sync with it.
|
|
14
|
+
*
|
|
15
|
+
* Every bytes32 field must be lowercase hex. The backend decodes them case-insensitively but hashes its own
|
|
16
|
+
* re-serialization, which is always lowercase, so a mixed-case value is accepted on the wire and then fails
|
|
17
|
+
* authentication against a hash the client never reproduced. Build them with `subaccountToHex`, which
|
|
18
|
+
* lowercases; unlike bytes32, string fields such as `display_name` are hashed as-is and keep their casing.
|
|
14
19
|
*/
|
|
15
20
|
export type MobileSignedInner =
|
|
16
21
|
| { type: 'set_username'; display_name: string }
|
|
@@ -23,14 +28,42 @@ export type MobileSignedInner =
|
|
|
23
28
|
// backend, which serializes `Option::None` as nil under the same key.
|
|
24
29
|
locale: string | null;
|
|
25
30
|
app_version: string | null;
|
|
31
|
+
}
|
|
32
|
+
| {
|
|
33
|
+
type: 'set_follow';
|
|
34
|
+
subaccount: Hex;
|
|
35
|
+
/** `true` follows the target, `false` unfollows it. Both directions are idempotent. */
|
|
36
|
+
is_following: boolean;
|
|
37
|
+
}
|
|
38
|
+
| {
|
|
39
|
+
type: 'followers';
|
|
40
|
+
subaccount: Hex;
|
|
41
|
+
/** `null` requests the first page; otherwise an opaque cursor from the previous page. */
|
|
42
|
+
cursor: string | null;
|
|
43
|
+
/** `null` requests the backend default of 25; otherwise 1–50. */
|
|
44
|
+
limit: number | null;
|
|
45
|
+
}
|
|
46
|
+
| {
|
|
47
|
+
type: 'following';
|
|
48
|
+
subaccount: Hex;
|
|
49
|
+
/** `null` requests the first page; otherwise an opaque cursor from the previous page. */
|
|
50
|
+
cursor: string | null;
|
|
51
|
+
/** `null` requests the backend default of 25; otherwise 1–50. */
|
|
52
|
+
limit: number | null;
|
|
26
53
|
};
|
|
27
54
|
|
|
55
|
+
/**
|
|
56
|
+
* The single {@link MobileSignedInner} member carrying the given `type` tag.
|
|
57
|
+
*/
|
|
58
|
+
export type MobileSignedInnerByType<T extends MobileSignedInner['type']> =
|
|
59
|
+
Extract<MobileSignedInner, { type: T }>;
|
|
60
|
+
|
|
28
61
|
/**
|
|
29
62
|
* Type-specific payload fields for a signed inner request of a given `type` (the inner payload without its
|
|
30
63
|
* `type` tag). Derived from {@link MobileSignedInner} so the two never drift apart.
|
|
31
64
|
*/
|
|
32
65
|
export type MobileSignedInnerParams<T extends MobileSignedInner['type']> = Omit<
|
|
33
|
-
|
|
66
|
+
MobileSignedInnerByType<T>,
|
|
34
67
|
'type'
|
|
35
68
|
>;
|
|
36
69
|
|
package/src/types/clientTypes.ts
CHANGED
|
@@ -9,7 +9,11 @@ import {
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* A subaccount's public profile, as returned by an unsigned profile lookup. Private Mode hides the account's
|
|
12
|
-
* activity, not its profile, so `privateMode` is exposed here too.
|
|
12
|
+
* activity, not its profile, so `privateMode` is exposed here too. A subaccount with no identity row still
|
|
13
|
+
* yields a profile, with `null` names and `privateMode: false`.
|
|
14
|
+
*
|
|
15
|
+
* The last three fields are `undefined` unless the matching {@link MobileProfilesInclude} asked for them —
|
|
16
|
+
* absent means "not requested" rather than "none".
|
|
13
17
|
*/
|
|
14
18
|
export interface MobilePublicProfile {
|
|
15
19
|
subaccount: Hex;
|
|
@@ -18,6 +22,80 @@ export interface MobilePublicProfile {
|
|
|
18
22
|
/** User-facing name as claimed, or `null` if no username has been claimed. */
|
|
19
23
|
displayName: string | null;
|
|
20
24
|
privateMode: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Exact count at query time rather than a cached counter, including unnamed and private accounts — Private
|
|
27
|
+
* Mode hides activity, not relationships. Requires the `followCounts` include.
|
|
28
|
+
*/
|
|
29
|
+
followerCount?: number;
|
|
30
|
+
followingCount?: number;
|
|
31
|
+
/** Requires the `followSummary` include, and is relative to that include's `viewAs`. */
|
|
32
|
+
followSummary?: MobileFollowSummary;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The identity fields the backend attaches to a subaccount wherever it surfaces one alongside other data.
|
|
37
|
+
* Resolved at read time, so a rename shows up on the next read without any change to the surrounding record.
|
|
38
|
+
*/
|
|
39
|
+
export interface MobileIdentitySummary {
|
|
40
|
+
subaccount: Hex;
|
|
41
|
+
/** `null` when the account has not claimed a username; render a local fallback label from `subaccount`. */
|
|
42
|
+
username: string | null;
|
|
43
|
+
/** `null` when the account has not claimed a username; render a local fallback label from `subaccount`. */
|
|
44
|
+
displayName: string | null;
|
|
45
|
+
/** Reserved for a future avatar source; `null` until one is implemented. */
|
|
46
|
+
avatarUrl: string | null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A viewer's relationship with one Profile, plus the Followed By preview: accounts the viewer follows that
|
|
51
|
+
* also follow that Profile. Every preview entry is therefore already familiar to the viewer, which is why it
|
|
52
|
+
* carries no `isFollowing` of its own.
|
|
53
|
+
*/
|
|
54
|
+
export interface MobileFollowSummary {
|
|
55
|
+
/** The direct `viewer -> viewed Profile` relationship. */
|
|
56
|
+
isFollowing: boolean;
|
|
57
|
+
/** Exact size of the two-edge intersection, independent of how many previews came back. */
|
|
58
|
+
followedByCount: number;
|
|
59
|
+
/**
|
|
60
|
+
* Newest `preview -> viewed Profile` relationship first, with a bytes32 tie-break. Capped at
|
|
61
|
+
* `MOBILE_FOLLOWED_BY_PREVIEW_LIMIT` (2) by the backend and not client-configurable.
|
|
62
|
+
*/
|
|
63
|
+
followedBy: MobileIdentitySummary[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* One row of a Followers or Following page.
|
|
68
|
+
*/
|
|
69
|
+
export interface MobileFollowListAccount {
|
|
70
|
+
identity: MobileIdentitySummary;
|
|
71
|
+
/**
|
|
72
|
+
* Whether the *Viewer* follows this account — not the listed relationship that put it in the page. The
|
|
73
|
+
* Viewer can appear in another Profile's list, and does not follow themself, so their own row is `false`.
|
|
74
|
+
*/
|
|
75
|
+
isFollowing: boolean;
|
|
76
|
+
/** This account's own follower count. */
|
|
77
|
+
followerCount: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A page of Followers or Following. Pagination is live rather than snapshot: a Follow or Unfollow between
|
|
82
|
+
* pages can move an account, so an account can appear twice or not at all across one session. Deduplicate by
|
|
83
|
+
* {@link MobileIdentitySummary.subaccount} and restart from `cursor: undefined` on refresh.
|
|
84
|
+
*/
|
|
85
|
+
export interface MobileFollowListPage {
|
|
86
|
+
accounts: MobileFollowListAccount[];
|
|
87
|
+
/** `null` means the list is complete. */
|
|
88
|
+
nextCursor: string | null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Post-commit state of a follow or unfollow. The mutation and its count share one database transaction, so
|
|
93
|
+
* these values are authoritative — apply them rather than re-reading.
|
|
94
|
+
*/
|
|
95
|
+
export interface MobileFollowMutationResult {
|
|
96
|
+
isFollowing: boolean;
|
|
97
|
+
/** The *target's* follower count after the mutation, not the sender's. */
|
|
98
|
+
followerCount: number;
|
|
21
99
|
}
|
|
22
100
|
|
|
23
101
|
/**
|
|
@@ -43,16 +121,9 @@ export interface MobileFeedMargin {
|
|
|
43
121
|
* (a rename can change an already-fetched trade on the next read). Amounts are display-oriented human-unit
|
|
44
122
|
* numbers — do not use them for accounting, order construction, or exact threshold decisions.
|
|
45
123
|
*/
|
|
46
|
-
export interface MobileFeedTrade {
|
|
124
|
+
export interface MobileFeedTrade extends MobileIdentitySummary {
|
|
47
125
|
/** Order digest this trade is keyed by; the stable id for reconciling live pagination. */
|
|
48
126
|
orderDigest: Hex;
|
|
49
|
-
subaccount: Hex;
|
|
50
|
-
/** `null` when the trader has not claimed a username; render a local fallback label from `subaccount`. */
|
|
51
|
-
username: string | null;
|
|
52
|
-
/** `null` when the trader has not claimed a username; render a local fallback label from `subaccount`. */
|
|
53
|
-
displayName: string | null;
|
|
54
|
-
/** Reserved for a future avatar source; `null` until one is implemented. */
|
|
55
|
-
avatarUrl: string | null;
|
|
56
127
|
productId: number;
|
|
57
128
|
/** Executed quantity in human units. */
|
|
58
129
|
quantity: number;
|
|
@@ -155,9 +226,31 @@ export interface GetMobileUsernameAvailabilityParams {
|
|
|
155
226
|
}
|
|
156
227
|
|
|
157
228
|
/**
|
|
158
|
-
*
|
|
229
|
+
* Opt-in extras for a profiles lookup. Each one adds fields that are otherwise absent from every returned
|
|
230
|
+
* profile, and costs extra rate-limit weight per profile, so ask only for what the UI shows.
|
|
159
231
|
*/
|
|
160
|
-
export
|
|
232
|
+
export interface MobileProfilesInclude {
|
|
233
|
+
/** Populates `followerCount` and `followingCount`. */
|
|
234
|
+
followCounts?: boolean;
|
|
235
|
+
/**
|
|
236
|
+
* Populates `followSummary`, relative to `viewAs`. Unlike the signed follow list reads, this viewer
|
|
237
|
+
* identity is an unauthenticated claim — the route takes no signature — so do not treat the result as
|
|
238
|
+
* proof of who is asking.
|
|
239
|
+
*/
|
|
240
|
+
followSummary?: { viewAs: Subaccount };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Params for {@link MobileClient.getProfiles}.
|
|
245
|
+
*/
|
|
246
|
+
export interface GetMobileProfilesParams {
|
|
247
|
+
/**
|
|
248
|
+
* 1–`MOBILE_PROFILES_MAX_BATCH_SIZE` (25) distinct subaccounts. Duplicates and an empty list are rejected
|
|
249
|
+
* rather than deduplicated, and results come back in this exact order.
|
|
250
|
+
*/
|
|
251
|
+
subaccounts: Subaccount[];
|
|
252
|
+
include?: MobileProfilesInclude;
|
|
253
|
+
}
|
|
161
254
|
|
|
162
255
|
/**
|
|
163
256
|
* Params for {@link MobileClient.getFeed}.
|
|
@@ -192,6 +285,50 @@ export interface MobileSetPrivateModeParams extends MobileSignedRequestParams {
|
|
|
192
285
|
privateMode: boolean;
|
|
193
286
|
}
|
|
194
287
|
|
|
288
|
+
/**
|
|
289
|
+
* Common params for the signed follow requests. The subaccount carried by {@link MobileSignedRequestParams}
|
|
290
|
+
* is the signing Viewer (the Follower, for mutations); `target` is the other party.
|
|
291
|
+
*/
|
|
292
|
+
export interface MobileFollowRequestParams extends MobileSignedRequestParams {
|
|
293
|
+
/** The Profile being followed, unfollowed, or read. Must not be the signing subaccount. */
|
|
294
|
+
target: Subaccount;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Params for {@link MobileClient.setFollow}.
|
|
299
|
+
*/
|
|
300
|
+
export interface MobileSetFollowParams extends MobileFollowRequestParams {
|
|
301
|
+
/** `true` follows the target, `false` unfollows it. Both directions are idempotent. */
|
|
302
|
+
isFollowing: boolean;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Common params for a Followers or Following page.
|
|
307
|
+
*/
|
|
308
|
+
export interface GetMobileFollowListParams extends MobileFollowRequestParams {
|
|
309
|
+
/**
|
|
310
|
+
* Opaque cursor from the previous page's {@link MobileFollowListPage.nextCursor}. Omit for the first page.
|
|
311
|
+
* A cursor is bound to its Viewer, Profile, and list direction — reusing it elsewhere fails with
|
|
312
|
+
* `INVALID_FOLLOW_CURSOR`.
|
|
313
|
+
*/
|
|
314
|
+
cursor?: string;
|
|
315
|
+
/**
|
|
316
|
+
* Page size, 1–`MOBILE_FOLLOW_LIST_MAX_PAGE_SIZE` (50); the backend defaults to
|
|
317
|
+
* `MOBILE_FOLLOW_LIST_DEFAULT_PAGE_SIZE` (25) when omitted.
|
|
318
|
+
*/
|
|
319
|
+
limit?: number;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Params for {@link MobileClient.getFollowers}.
|
|
324
|
+
*/
|
|
325
|
+
export type GetMobileFollowersParams = GetMobileFollowListParams;
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Params for {@link MobileClient.getFollowing}.
|
|
329
|
+
*/
|
|
330
|
+
export type GetMobileFollowingParams = GetMobileFollowListParams;
|
|
331
|
+
|
|
195
332
|
/**
|
|
196
333
|
* Params for {@link MobileClient.registerExpoToken}.
|
|
197
334
|
*/
|
|
@@ -13,6 +13,18 @@ export const MOBILE_ERROR_CODES = {
|
|
|
13
13
|
/** Another identity write for the subaccount committed with a later nonce. Re-read before retrying. */
|
|
14
14
|
STALE_IDENTITY_UPDATE: 6105,
|
|
15
15
|
PROFILE_NOT_FOUND: 6106,
|
|
16
|
+
/** Profiles lookup is empty, holds duplicates, or exceeds the batch maximum. Do not retry unchanged. */
|
|
17
|
+
INVALID_PROFILES_REQUEST: 6107,
|
|
18
|
+
/** Follow mutation names the same source and target, or an isolated participant. Do not retry unchanged. */
|
|
19
|
+
INVALID_FOLLOW_TARGET: 6110,
|
|
20
|
+
/** Signed follower is not yet in the canonical Query DB subaccount index. Retry after index catch-up. */
|
|
21
|
+
FOLLOWER_NOT_ELIGIBLE: 6111,
|
|
22
|
+
/** Follow target is not yet in the canonical Query DB subaccount index. Refresh after a new record. */
|
|
23
|
+
FOLLOWING_NOT_FOUND: 6112,
|
|
24
|
+
/** Follow list cursor is malformed or bound to a different list request. Restart from `cursor: null`. */
|
|
25
|
+
INVALID_FOLLOW_CURSOR: 6113,
|
|
26
|
+
/** A follow list or followed-by limit is outside its accepted range. Fix it; do not retry unchanged. */
|
|
27
|
+
INVALID_FOLLOW_LIMIT: 6114,
|
|
16
28
|
INVALID_EXPO_TOKEN: 6200,
|
|
17
29
|
INVALID_DEVICE_METADATA: 6201,
|
|
18
30
|
INVALID_PREFERENCES: 6202,
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
MobileServerSuccessResponse,
|
|
4
4
|
} from './serverBaseTypes';
|
|
5
5
|
import {
|
|
6
|
+
MobileServerExecuteRequestType,
|
|
6
7
|
MobileServerPublicExecuteRequestByType,
|
|
7
8
|
MobileServerPublicExecuteRequestType,
|
|
8
9
|
} from './serverRequestTypes';
|
|
@@ -21,8 +22,45 @@ export type MobileServerPublicExecuteRequest<
|
|
|
21
22
|
}[T];
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
25
|
+
* Payload the `set_follow` execute returns alongside the success envelope. The mutation and the count commit
|
|
26
|
+
* in one database transaction, so these are the authoritative post-mutation values — prefer them over
|
|
27
|
+
* re-reading, and note that overlapping toggles for the same pair can respond out of arrival order.
|
|
28
|
+
*/
|
|
29
|
+
export interface MobileServerFollowMutationResponse {
|
|
30
|
+
is_following: boolean;
|
|
31
|
+
/** The *target's* follower count after the mutation, not the sender's. */
|
|
32
|
+
follower_count: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Payload of an execute route that returns nothing beyond the success discriminant. Spelled `Record<never,
|
|
37
|
+
* never>` rather than `Record<string, never>` because the latter's index signature would collapse `status` to
|
|
38
|
+
* `never` when intersected with the success envelope.
|
|
39
|
+
*/
|
|
40
|
+
export type MobileServerEmptyExecuteResponse = Record<never, never>;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Success payloads for each signed `execute`, keyed by request `type`. Most execute routes carry no payload
|
|
44
|
+
* beyond the success discriminant and so map to an empty object; `set_follow` returns its post-commit
|
|
45
|
+
* relationship state.
|
|
46
|
+
*/
|
|
47
|
+
export interface MobileServerExecuteResponseByType {
|
|
48
|
+
set_username: MobileServerEmptyExecuteResponse;
|
|
49
|
+
set_private_mode: MobileServerEmptyExecuteResponse;
|
|
50
|
+
register_expo_token: MobileServerEmptyExecuteResponse;
|
|
51
|
+
set_follow: MobileServerFollowMutationResponse;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Full success response for a signed `execute`: the {@link MobileServerSuccessResponse} envelope with the
|
|
56
|
+
* route's payload inlined alongside `status`, matching how the query routes inline theirs.
|
|
57
|
+
*/
|
|
58
|
+
export type MobileServerExecuteSuccessResponse<
|
|
59
|
+
T extends MobileServerExecuteRequestType = MobileServerExecuteRequestType,
|
|
60
|
+
> = MobileServerSuccessResponse & MobileServerExecuteResponseByType[T];
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Discriminated `execute` result union used at the transport boundary, before the envelope is narrowed.
|
|
26
64
|
*/
|
|
27
65
|
export type MobileServerExecuteResult =
|
|
28
66
|
| MobileServerSuccessResponse
|
|
@@ -18,14 +18,66 @@ export type MobileNotificationCategory =
|
|
|
18
18
|
| 'announcement';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* Server-side
|
|
22
|
-
*
|
|
21
|
+
* Server-side identity summary (snake_case): the read-time identity fields the backend attaches to a
|
|
22
|
+
* subaccount wherever it surfaces one alongside other data. Name fields are `null` until the subaccount
|
|
23
|
+
* claims a username, and a rename shows up on the next read without any change to the surrounding record.
|
|
24
|
+
*/
|
|
25
|
+
export interface MobileServerIdentitySummary {
|
|
26
|
+
subaccount: Hex;
|
|
27
|
+
username: string | null;
|
|
28
|
+
display_name: string | null;
|
|
29
|
+
/** Reserved for a future avatar source; `null` until one is implemented. */
|
|
30
|
+
avatar_url: string | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Server-side follow summary (snake_case), nested in a `profiles` entry when the `follow_summary` include is
|
|
35
|
+
* requested. `is_following` is the direct `view_as -> this profile` relationship; every `followed_by` entry
|
|
36
|
+
* satisfies both `view_as -> entry` and `entry -> this profile`, so each one is already familiar to the
|
|
37
|
+
* viewer and carries no `is_following` of its own.
|
|
38
|
+
*/
|
|
39
|
+
export interface MobileServerFollowSummary {
|
|
40
|
+
is_following: boolean;
|
|
41
|
+
/** Exact size of the two-edge intersection, independent of how many previews came back. */
|
|
42
|
+
followed_by_count: number;
|
|
43
|
+
/** Capped at `MOBILE_FOLLOWED_BY_PREVIEW_LIMIT` (2) by the backend; not client-configurable. */
|
|
44
|
+
followed_by: MobileServerIdentitySummary[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Server-side public profile shape (snake_case), shared by the singular `profile` and batched `profiles`
|
|
49
|
+
* queries. Name fields are `null` until the subaccount claims a username; a subaccount with no identity row
|
|
50
|
+
* still yields an entry, with `null` names and `private_mode: false`.
|
|
51
|
+
*
|
|
52
|
+
* The three optional fields are omitted from the wire entirely unless the matching `profiles` include asked
|
|
53
|
+
* for them, which is why they are optional rather than nullable — absent means "not requested", not "none".
|
|
54
|
+
* The singular `profile` query never returns any of them.
|
|
23
55
|
*/
|
|
24
56
|
export interface MobileServerProfile {
|
|
25
57
|
subaccount: Hex;
|
|
26
58
|
username: string | null;
|
|
27
59
|
display_name: string | null;
|
|
28
60
|
private_mode: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Exact count at query time — the backend counts rather than reading a cached counter. Unnamed and private
|
|
63
|
+
* accounts are included: Private Mode hides activity, not relationships. Requires `follow_counts`.
|
|
64
|
+
*/
|
|
65
|
+
follower_count?: number;
|
|
66
|
+
following_count?: number;
|
|
67
|
+
/** Requires the `follow_summary` include, and is relative to that include's `view_as`. */
|
|
68
|
+
follow_summary?: MobileServerFollowSummary;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Server-side row of a Followers or Following page (snake_case). `is_following` describes the *Viewer's*
|
|
73
|
+
* relationship to this account, not the listed relationship that put it in the page, so it is `false` on the
|
|
74
|
+
* Viewer's own row.
|
|
75
|
+
*/
|
|
76
|
+
export interface MobileServerFollowListAccount {
|
|
77
|
+
identity: MobileServerIdentitySummary;
|
|
78
|
+
is_following: boolean;
|
|
79
|
+
/** The listed account's own follower count, exact at query time. */
|
|
80
|
+
follower_count: number;
|
|
29
81
|
}
|
|
30
82
|
|
|
31
83
|
/**
|
|
@@ -97,14 +149,9 @@ export interface MobileServerFeedMargin {
|
|
|
97
149
|
* trader's current identity. Amounts are display-oriented human-unit JSON numbers — do not use them for
|
|
98
150
|
* accounting, order construction, or exact threshold decisions.
|
|
99
151
|
*/
|
|
100
|
-
export interface MobileServerFeedTrade {
|
|
152
|
+
export interface MobileServerFeedTrade extends MobileServerIdentitySummary {
|
|
101
153
|
/** Order digest this row is keyed by; the stable id clients use to reconcile live pagination. */
|
|
102
154
|
order_digest: Hex;
|
|
103
|
-
subaccount: Hex;
|
|
104
|
-
username: string | null;
|
|
105
|
-
display_name: string | null;
|
|
106
|
-
/** Reserved for a future avatar source; `null` until one is implemented. */
|
|
107
|
-
avatar_url: string | null;
|
|
108
155
|
product_id: number;
|
|
109
156
|
/** Executed quantity in human units. */
|
|
110
157
|
quantity: number;
|
|
@@ -3,12 +3,14 @@ import { MobileServerSuccessResponse } from './serverBaseTypes';
|
|
|
3
3
|
import {
|
|
4
4
|
MobileNotificationPlatform,
|
|
5
5
|
MobileServerFeedTrade,
|
|
6
|
+
MobileServerFollowListAccount,
|
|
6
7
|
MobileServerNotificationPreferences,
|
|
7
8
|
MobileServerProfile,
|
|
8
9
|
} from './serverModelTypes';
|
|
9
10
|
import {
|
|
10
11
|
MobileServerPublicQueryRequestByType,
|
|
11
12
|
MobileServerPublicQueryRequestType,
|
|
13
|
+
MobileServerSignedQueryRequestType,
|
|
12
14
|
} from './serverRequestTypes';
|
|
13
15
|
|
|
14
16
|
/**
|
|
@@ -32,10 +34,11 @@ export interface MobileServerUsernameAvailabilityResponse {
|
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
/**
|
|
35
|
-
* Payload of the `
|
|
37
|
+
* Payload of the `profiles` public query success response. One entry per requested subaccount, in the exact
|
|
38
|
+
* order they were requested, so callers can correlate positionally as well as by `subaccount`.
|
|
36
39
|
*/
|
|
37
|
-
export interface
|
|
38
|
-
|
|
40
|
+
export interface MobileServerProfilesResponse {
|
|
41
|
+
profiles: MobileServerProfile[];
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
/**
|
|
@@ -79,7 +82,7 @@ export interface MobileServerRegisteredWalletResponse {
|
|
|
79
82
|
*/
|
|
80
83
|
export interface MobileServerPublicQueryResponseByType {
|
|
81
84
|
username_availability: MobileServerUsernameAvailabilityResponse;
|
|
82
|
-
|
|
85
|
+
profiles: MobileServerProfilesResponse;
|
|
83
86
|
feed: MobileServerFeedResponse;
|
|
84
87
|
notification_preferences: MobileServerNotificationPreferencesResponse;
|
|
85
88
|
registered_wallet: MobileServerRegisteredWalletResponse;
|
|
@@ -94,3 +97,31 @@ export type MobileServerPublicQuerySuccessResponse<
|
|
|
94
97
|
T extends MobileServerPublicQueryRequestType =
|
|
95
98
|
MobileServerPublicQueryRequestType,
|
|
96
99
|
> = MobileServerSuccessResponse & MobileServerPublicQueryResponseByType[T];
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Payload of the `followers` and `following` signed query success responses. Both list directions share one
|
|
103
|
+
* shape; only the relationship that selected the rows differs.
|
|
104
|
+
*/
|
|
105
|
+
export interface MobileServerFollowListResponse {
|
|
106
|
+
accounts: MobileServerFollowListAccount[];
|
|
107
|
+
/** `null` means the list is complete. */
|
|
108
|
+
next_cursor: string | null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Success payloads for each signed `query`, keyed by request `type` — the signed counterpart to
|
|
113
|
+
* {@link MobileServerPublicQueryResponseByType}.
|
|
114
|
+
*/
|
|
115
|
+
export interface MobileServerSignedQueryResponseByType {
|
|
116
|
+
followers: MobileServerFollowListResponse;
|
|
117
|
+
following: MobileServerFollowListResponse;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Full success response for a signed `query`: the {@link MobileServerSuccessResponse} envelope with the
|
|
122
|
+
* query's payload inlined alongside `status`.
|
|
123
|
+
*/
|
|
124
|
+
export type MobileServerSignedQuerySuccessResponse<
|
|
125
|
+
T extends MobileServerSignedQueryRequestType =
|
|
126
|
+
MobileServerSignedQueryRequestType,
|
|
127
|
+
> = MobileServerSuccessResponse & MobileServerSignedQueryResponseByType[T];
|
|
@@ -2,12 +2,34 @@ import { Hex } from 'viem';
|
|
|
2
2
|
import { MobileSignedInner } from '../signing/types';
|
|
3
3
|
import { MobileServerNotificationPreferences } from './serverModelTypes';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Opt-in extras for a `profiles` query. Each one costs extra rate-limit weight per profile and adds fields
|
|
7
|
+
* that are otherwise absent from the response, so the backend gates them behind an explicit request rather
|
|
8
|
+
* than always resolving the follow graph on a public route.
|
|
9
|
+
*/
|
|
10
|
+
export interface MobileServerProfilesInclude {
|
|
11
|
+
/** Adds `follower_count` and `following_count` to every entry. Omitted or `false` leaves both absent. */
|
|
12
|
+
follow_counts?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Adds `follow_summary` to every entry, relative to the `view_as` subaccount. Unlike the signed follow
|
|
15
|
+
* list reads, `view_as` is an unauthenticated claim on this public route.
|
|
16
|
+
*/
|
|
17
|
+
follow_summary?: { view_as: Hex };
|
|
18
|
+
}
|
|
19
|
+
|
|
5
20
|
/**
|
|
6
21
|
* Params for each unsigned `public_query`, keyed by request `type`.
|
|
7
22
|
*/
|
|
8
23
|
export interface MobileServerPublicQueryRequestByType {
|
|
9
24
|
username_availability: { display_name: string };
|
|
10
|
-
|
|
25
|
+
profiles: {
|
|
26
|
+
/**
|
|
27
|
+
* 1–`MOBILE_PROFILES_MAX_BATCH_SIZE` (25) distinct subaccounts. Results come back in this exact order,
|
|
28
|
+
* one entry per request slot.
|
|
29
|
+
*/
|
|
30
|
+
subaccounts: Hex[];
|
|
31
|
+
include?: MobileServerProfilesInclude;
|
|
32
|
+
};
|
|
11
33
|
feed: {
|
|
12
34
|
/**
|
|
13
35
|
* Minimum notional as a whole-dollar JSON integer (NOT an x18 string), at least $1,000. Omitted or
|
|
@@ -52,18 +74,31 @@ export interface MobileServerPublicExecuteRequestByType {
|
|
|
52
74
|
export type MobileServerPublicExecuteRequestType =
|
|
53
75
|
keyof MobileServerPublicExecuteRequestByType;
|
|
54
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Discriminant `type` values for signed `query` requests: the read-only subset of {@link MobileSignedInner}
|
|
79
|
+
* tags. Unlike the public queries, these carry no separate params map — a signed request body *is* its inner
|
|
80
|
+
* payload flattened with the envelope, so {@link MobileSignedInner} is the single source of truth for both
|
|
81
|
+
* the fields and their significant order.
|
|
82
|
+
*/
|
|
83
|
+
export type MobileServerSignedQueryRequestType = 'followers' | 'following';
|
|
84
|
+
|
|
55
85
|
/**
|
|
56
86
|
* Discriminant `type` values for signed `execute` requests: the write subset of {@link MobileSignedInner}
|
|
57
|
-
* tags.
|
|
87
|
+
* tags, i.e. everything that is not a signed query.
|
|
58
88
|
*/
|
|
59
|
-
export type MobileServerExecuteRequestType =
|
|
89
|
+
export type MobileServerExecuteRequestType = Exclude<
|
|
90
|
+
MobileSignedInner['type'],
|
|
91
|
+
MobileServerSignedQueryRequestType
|
|
92
|
+
>;
|
|
60
93
|
|
|
61
94
|
/**
|
|
62
95
|
* Wire `request_type` the backend echoes on failure envelopes, prefixed by the route that produced it:
|
|
63
|
-
* `public_query_*` for unsigned queries, `public_execute_*` for unsigned writes,
|
|
64
|
-
* writes (e.g. `execute_set_username`).
|
|
96
|
+
* `public_query_*` for unsigned queries, `public_execute_*` for unsigned writes, `execute_*` for signed
|
|
97
|
+
* writes (e.g. `execute_set_username`), and `query_*` for signed reads (e.g. `query_followers`). The prefix
|
|
98
|
+
* names the route, so it never carries the `mobile:` prefix that the EIP-712 method does.
|
|
65
99
|
*/
|
|
66
100
|
export type MobileServerRequestType =
|
|
67
101
|
| `public_query_${MobileServerPublicQueryRequestType}`
|
|
68
102
|
| `public_execute_${MobileServerPublicExecuteRequestType}`
|
|
69
|
-
| `execute_${MobileServerExecuteRequestType}
|
|
103
|
+
| `execute_${MobileServerExecuteRequestType}`
|
|
104
|
+
| `query_${MobileServerSignedQueryRequestType}`;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maximum Followers/Following page size the backend accepts. Requests outside 1–50 fail with
|
|
3
|
+
* `INVALID_FOLLOW_LIMIT`.
|
|
4
|
+
*/
|
|
5
|
+
export const MOBILE_FOLLOW_LIST_MAX_PAGE_SIZE = 50;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Page size the backend uses when `limit` is omitted from a Followers or Following request.
|
|
9
|
+
*/
|
|
10
|
+
export const MOBILE_FOLLOW_LIST_DEFAULT_PAGE_SIZE = 25;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Number of Followed By preview identities a follow summary carries. Fixed by the backend rather than
|
|
14
|
+
* requested, so use it to size the preview UI; `followedByCount` still reports the full intersection.
|
|
15
|
+
*/
|
|
16
|
+
export const MOBILE_FOLLOWED_BY_PREVIEW_LIMIT = 2;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Maximum number of subaccounts a single profiles lookup accepts. An empty list, a duplicate, or more than
|
|
20
|
+
* this many fail with `INVALID_PROFILES_REQUEST`.
|
|
21
|
+
*/
|
|
22
|
+
export const MOBILE_PROFILES_MAX_BATCH_SIZE = 25;
|
package/src/utils/index.ts
CHANGED