@aboutcircles/sdk-rpc 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 (68) hide show
  1. package/README.md +169 -0
  2. package/dist/client.d.ts +58 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +194 -0
  5. package/dist/errors.d.ts +44 -0
  6. package/dist/errors.d.ts.map +1 -0
  7. package/dist/errors.js +63 -0
  8. package/dist/events/index.d.ts +8 -0
  9. package/dist/events/index.d.ts.map +1 -0
  10. package/dist/events/index.js +8 -0
  11. package/dist/events/observable.d.ts +23 -0
  12. package/dist/events/observable.d.ts.map +1 -0
  13. package/dist/events/observable.js +37 -0
  14. package/dist/events/parser.d.ts +10 -0
  15. package/dist/events/parser.d.ts.map +1 -0
  16. package/dist/events/parser.js +103 -0
  17. package/dist/events/types.d.ts +7 -0
  18. package/dist/events/types.d.ts.map +1 -0
  19. package/dist/events/types.js +11 -0
  20. package/dist/index.d.ts +12 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +2654 -0
  23. package/dist/methods/avatar.d.ts +51 -0
  24. package/dist/methods/avatar.d.ts.map +1 -0
  25. package/dist/methods/avatar.js +64 -0
  26. package/dist/methods/balance.d.ts +37 -0
  27. package/dist/methods/balance.d.ts.map +1 -0
  28. package/dist/methods/balance.js +50 -0
  29. package/dist/methods/group.d.ts +145 -0
  30. package/dist/methods/group.d.ts.map +1 -0
  31. package/dist/methods/group.js +380 -0
  32. package/dist/methods/index.d.ts +11 -0
  33. package/dist/methods/index.d.ts.map +1 -0
  34. package/dist/methods/index.js +10 -0
  35. package/dist/methods/invitation.d.ts +61 -0
  36. package/dist/methods/invitation.d.ts.map +1 -0
  37. package/dist/methods/invitation.js +230 -0
  38. package/dist/methods/pathfinder.d.ts +41 -0
  39. package/dist/methods/pathfinder.d.ts.map +1 -0
  40. package/dist/methods/pathfinder.js +53 -0
  41. package/dist/methods/profile.d.ts +109 -0
  42. package/dist/methods/profile.d.ts.map +1 -0
  43. package/dist/methods/profile.js +166 -0
  44. package/dist/methods/query.d.ts +79 -0
  45. package/dist/methods/query.d.ts.map +1 -0
  46. package/dist/methods/query.js +87 -0
  47. package/dist/methods/token.d.ts +61 -0
  48. package/dist/methods/token.d.ts.map +1 -0
  49. package/dist/methods/token.js +99 -0
  50. package/dist/methods/transaction.d.ts +41 -0
  51. package/dist/methods/transaction.d.ts.map +1 -0
  52. package/dist/methods/transaction.js +111 -0
  53. package/dist/methods/trust.d.ts +114 -0
  54. package/dist/methods/trust.d.ts.map +1 -0
  55. package/dist/methods/trust.js +245 -0
  56. package/dist/pagedQuery.d.ts +106 -0
  57. package/dist/pagedQuery.d.ts.map +1 -0
  58. package/dist/pagedQuery.js +254 -0
  59. package/dist/rpc.d.ts +61 -0
  60. package/dist/rpc.d.ts.map +1 -0
  61. package/dist/rpc.js +76 -0
  62. package/dist/types.d.ts +82 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +1 -0
  65. package/dist/utils.d.ts +27 -0
  66. package/dist/utils.d.ts.map +1 -0
  67. package/dist/utils.js +111 -0
  68. package/package.json +32 -0
@@ -0,0 +1,245 @@
1
+ import { normalizeAddress, checksumAddresses } from '../utils';
2
+ import { PagedQuery } from '../pagedQuery';
3
+ /**
4
+ * Trust relation RPC methods
5
+ */
6
+ export class TrustMethods {
7
+ client;
8
+ constructor(client) {
9
+ this.client = client;
10
+ }
11
+ transformQueryResponse(response) {
12
+ const { columns, rows } = response;
13
+ return rows.map((row) => {
14
+ const obj = {};
15
+ columns.forEach((col, index) => {
16
+ obj[col] = row[index];
17
+ });
18
+ return obj;
19
+ });
20
+ }
21
+ /**
22
+ * Query the common trust relations of two addresses
23
+ * (only common outgoing trust relations are considered)
24
+ *
25
+ * @param address1 - First address
26
+ * @param address2 - Second address
27
+ * @returns Array of common trusted addresses
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const commonTrust = await rpc.trust.getCommonTrust(
32
+ * '0xde374ece6fa50e781e81aac78e811b33d16912c7',
33
+ * '0xe8fc7a2d0573e5164597b05f14fa9a7fca7b215c'
34
+ * );
35
+ * ```
36
+ */
37
+ // @todo check if we need to normilize addr before the call
38
+ async getCommonTrust(address1, address2) {
39
+ const result = await this.client.call('circles_getCommonTrust', [
40
+ normalizeAddress(address1),
41
+ normalizeAddress(address2),
42
+ ]);
43
+ return checksumAddresses(result);
44
+ }
45
+ /**
46
+ * Get trust relations for an address using cursor-based pagination
47
+ *
48
+ * Returns a PagedQuery instance for iterating through all v2 trust relations for the given avatar.
49
+ *
50
+ * @param avatar - Avatar address to query trust relations for
51
+ * @param limit - Number of trust relations per page (default: 100)
52
+ * @param sortOrder - Sort order for results (default: 'DESC')
53
+ * @returns PagedQuery instance for iterating through trust relations
54
+ *
55
+ * @example
56
+ * ```typescript
57
+ * const query = rpc.trust.getTrustRelations('0xAvatar...', 100);
58
+ *
59
+ * // Get first page
60
+ * await query.queryNextPage();
61
+ * query.currentPage.results.forEach(relation => {
62
+ * console.log(`${relation.truster} trusts ${relation.trustee}`);
63
+ * });
64
+ * ```
65
+ */
66
+ getTrustRelations(avatar, limit = 100, sortOrder = 'DESC') {
67
+ const normalized = normalizeAddress(avatar);
68
+ const filter = [
69
+ {
70
+ Type: 'Conjunction',
71
+ ConjunctionType: 'And',
72
+ Predicates: [
73
+ {
74
+ Type: 'FilterPredicate',
75
+ FilterType: 'Equals',
76
+ Column: 'version',
77
+ Value: 2,
78
+ },
79
+ {
80
+ Type: 'Conjunction',
81
+ ConjunctionType: 'Or',
82
+ Predicates: [
83
+ {
84
+ Type: 'FilterPredicate',
85
+ FilterType: 'Equals',
86
+ Column: 'trustee',
87
+ Value: normalized,
88
+ },
89
+ {
90
+ Type: 'FilterPredicate',
91
+ FilterType: 'Equals',
92
+ Column: 'truster',
93
+ Value: normalized,
94
+ },
95
+ ],
96
+ },
97
+ ],
98
+ },
99
+ ];
100
+ return new PagedQuery(this.client, {
101
+ namespace: 'V_Crc',
102
+ table: 'TrustRelations',
103
+ sortOrder,
104
+ columns: [
105
+ 'blockNumber',
106
+ 'timestamp',
107
+ 'transactionIndex',
108
+ 'logIndex',
109
+ 'transactionHash',
110
+ 'version',
111
+ 'trustee',
112
+ 'truster',
113
+ 'expiryTime',
114
+ ],
115
+ filter,
116
+ limit,
117
+ }, (row) => checksumAddresses(row));
118
+ }
119
+ /**
120
+ * Get aggregated trust relations for an address
121
+ * Groups trust relations by counterpart and determines relationship type
122
+ *
123
+ * Note: This method fetches ALL trust relations for aggregation.
124
+ *
125
+ * @param avatar - Avatar address to query trust relations for
126
+ * @returns Aggregated trust relations with relationship types
127
+ *
128
+ * @example
129
+ * ```typescript
130
+ * const aggregated = await rpc.trust.getAggregatedTrustRelations(
131
+ * '0xde374ece6fa50e781e81aac78e811b33d16912c7'
132
+ * );
133
+ * // Returns: [
134
+ * // { subjectAvatar: '0x...', relation: 'mutuallyTrusts', objectAvatar: '0x...', timestamp: 123 },
135
+ * // { subjectAvatar: '0x...', relation: 'trusts', objectAvatar: '0x...', timestamp: 456 }
136
+ * // ]
137
+ * ```
138
+ */
139
+ async getAggregatedTrustRelations(avatar) {
140
+ const normalized = normalizeAddress(avatar);
141
+ // Fetch all trust relations by paginating
142
+ const query = this.getTrustRelations(normalized, 1000);
143
+ const trustListRows = [];
144
+ while (await query.queryNextPage()) {
145
+ trustListRows.push(...query.currentPage.results);
146
+ if (!query.currentPage.hasMore)
147
+ break;
148
+ }
149
+ // Group trust list rows by counterpart avatar
150
+ const trustBucket = {};
151
+ trustListRows.forEach((row) => {
152
+ // Normalize addresses for comparison (both are already checksummed from getTrustRelations)
153
+ const trusterNorm = normalizeAddress(row.truster);
154
+ const trusteeNorm = normalizeAddress(row.trustee);
155
+ const counterpart = trusterNorm !== normalized ? row.truster : row.trustee;
156
+ if (!trustBucket[counterpart]) {
157
+ trustBucket[counterpart] = [];
158
+ }
159
+ trustBucket[counterpart].push(row);
160
+ });
161
+ // Determine trust relations
162
+ const result = Object.entries(trustBucket)
163
+ .filter(([address]) => normalizeAddress(address) !== normalized)
164
+ .map(([address, rows]) => {
165
+ const maxTimestamp = Math.max(...rows.map((o) => o.timestamp));
166
+ let relation;
167
+ if (rows.length === 2) {
168
+ relation = 'mutuallyTrusts';
169
+ }
170
+ else if (normalizeAddress(rows[0]?.trustee) === normalized) {
171
+ relation = 'trustedBy';
172
+ }
173
+ else if (normalizeAddress(rows[0]?.truster) === normalized) {
174
+ relation = 'trusts';
175
+ }
176
+ else {
177
+ throw new Error(`Unexpected trust list row. Couldn't determine trust relation.`);
178
+ }
179
+ return {
180
+ subjectAvatar: normalized,
181
+ relation,
182
+ objectAvatar: address,
183
+ timestamp: maxTimestamp,
184
+ };
185
+ });
186
+ return checksumAddresses(result);
187
+ }
188
+ /**
189
+ * Get addresses that trust the given avatar (incoming trust)
190
+ *
191
+ * @param avatar - Avatar address to query
192
+ * @returns Array of trust relations where others trust this avatar
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * const trustedBy = await rpc.trust.getTrustedBy(
197
+ * '0xde374ece6fa50e781e81aac78e811b33d16912c7'
198
+ * );
199
+ * ```
200
+ */
201
+ async getTrustedBy(avatar) {
202
+ const normalized = normalizeAddress(avatar);
203
+ const relations = await this.getAggregatedTrustRelations(normalized);
204
+ const filtered = relations.filter((r) => r.relation === 'trustedBy');
205
+ return checksumAddresses(filtered);
206
+ }
207
+ /**
208
+ * Get addresses that the given avatar trusts (outgoing trust)
209
+ *
210
+ * @param avatar - Avatar address to query
211
+ * @returns Array of trust relations where this avatar trusts others
212
+ *
213
+ * @example
214
+ * ```typescript
215
+ * const trusts = await rpc.trust.getTrusts(
216
+ * '0xde374ece6fa50e781e81aac78e811b33d16912c7'
217
+ * );
218
+ * ```
219
+ */
220
+ async getTrusts(avatar) {
221
+ const normalized = normalizeAddress(avatar);
222
+ const relations = await this.getAggregatedTrustRelations(normalized);
223
+ const filtered = relations.filter((r) => r.relation === 'trusts');
224
+ return checksumAddresses(filtered);
225
+ }
226
+ /**
227
+ * Get mutual trust relations for the given avatar
228
+ *
229
+ * @param avatar - Avatar address to query
230
+ * @returns Array of trust relations where both parties trust each other
231
+ *
232
+ * @example
233
+ * ```typescript
234
+ * const mutualTrusts = await rpc.trust.getMutualTrusts(
235
+ * '0xde374ece6fa50e781e81aac78e811b33d16912c7'
236
+ * );
237
+ * ```
238
+ */
239
+ async getMutualTrusts(avatar) {
240
+ const normalized = normalizeAddress(avatar);
241
+ const relations = await this.getAggregatedTrustRelations(normalized);
242
+ const filtered = relations.filter((r) => r.relation === 'mutuallyTrusts');
243
+ return checksumAddresses(filtered);
244
+ }
245
+ }
@@ -0,0 +1,106 @@
1
+ import type { RpcClient } from './client';
2
+ import type { PagedQueryParams, OrderBy } from '@aboutcircles/sdk-types';
3
+ import type { CursorColumn, FlexiblePagedResult } from './types';
4
+ /**
5
+ * A class for querying Circles RPC nodes with cursor-based pagination.
6
+ * Supports both event-based pagination (default) and custom cursor pagination (for view tables).
7
+ *
8
+ * @typeParam TRow The type of the rows returned by the query.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * // Event-based pagination
13
+ * const query = new PagedQuery<GroupMembershipRow>(rpc.client, {
14
+ * namespace: 'V_CrcV2',
15
+ * table: 'GroupMemberships',
16
+ * sortOrder: 'DESC',
17
+ * columns: ['blockNumber', 'transactionIndex', 'logIndex', 'group', 'member'],
18
+ * filter: [{ Type: 'FilterPredicate', FilterType: 'Equals', Column: 'group', Value: '0x...' }],
19
+ * limit: 100
20
+ * });
21
+ *
22
+ * // Custom cursor pagination (for view tables)
23
+ * const viewQuery = new PagedQuery<GroupTokenHolderRow>(rpc.client, {
24
+ * namespace: 'V_CrcV2',
25
+ * table: 'GroupTokenHoldersBalance',
26
+ * sortOrder: 'ASC',
27
+ * columns: ['group', 'holder', 'totalBalance'],
28
+ * cursorColumns: [{ name: 'holder', sortOrder: 'ASC' }],
29
+ * filter: [{ Type: 'FilterPredicate', FilterType: 'Equals', Column: 'group', Value: '0x...' }],
30
+ * limit: 100
31
+ * });
32
+ * ```
33
+ */
34
+ export declare class PagedQuery<TRow = any> {
35
+ private readonly params;
36
+ private readonly client;
37
+ private readonly rowTransformer?;
38
+ private readonly cursorColumns;
39
+ private readonly orderColumns?;
40
+ get currentPage(): FlexiblePagedResult<TRow> | undefined;
41
+ private _currentPage?;
42
+ constructor(client: RpcClient, params: PagedQueryParams & {
43
+ cursorColumns?: CursorColumn[];
44
+ orderColumns?: OrderBy[];
45
+ rowTransformer?: (row: any) => TRow;
46
+ }, rowTransformer?: (row: any) => TRow);
47
+ /**
48
+ * Builds cursor columns for event-based tables
49
+ */
50
+ private buildEventCursorColumns;
51
+ /**
52
+ * Transforms a cursor value for use in query filters
53
+ */
54
+ private transformCursorValue;
55
+ /**
56
+ * Creates an equality predicate for a cursor column
57
+ */
58
+ private createEqualityPredicate;
59
+ /**
60
+ * Creates a comparison predicate for a cursor column (> or <)
61
+ */
62
+ private createComparisonPredicate;
63
+ /**
64
+ * Builds cursor filter for pagination using composite cursor columns.
65
+ *
66
+ * Creates an OR conjunction of predicates for each cursor level:
67
+ * - First level: col1 > cursor.col1
68
+ * - Second level: col1 = cursor.col1 AND col2 > cursor.col2
69
+ * - Third level: col1 = cursor.col1 AND col2 = cursor.col2 AND col3 > cursor.col3
70
+ *
71
+ * This ensures correct pagination across all cursor columns.
72
+ */
73
+ private buildCursorFilter;
74
+ /**
75
+ * Builds the order by clause.
76
+ * If orderColumns are provided, uses those. Otherwise builds from cursor columns.
77
+ */
78
+ private buildOrderBy;
79
+ /**
80
+ * Combines base filters with cursor filter
81
+ */
82
+ private combineFilters;
83
+ /**
84
+ * Converts query response rows to typed objects
85
+ */
86
+ private rowsToObjects;
87
+ /**
88
+ * Extracts cursor values from a row
89
+ */
90
+ private rowToCursor;
91
+ /**
92
+ * Gets first and last cursors from result set
93
+ */
94
+ private getCursors;
95
+ /**
96
+ * Queries the next page of results.
97
+ *
98
+ * @returns True if results were found, false otherwise
99
+ */
100
+ queryNextPage(): Promise<boolean>;
101
+ /**
102
+ * Resets the query to start from the beginning
103
+ */
104
+ reset(): void;
105
+ }
106
+ //# sourceMappingURL=pagedQuery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagedQuery.d.ts","sourceRoot":"","sources":["../src/pagedQuery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,KAAK,EACV,gBAAgB,EAEhB,OAAO,EAGR,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAWjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,UAAU,CAAC,IAAI,GAAG,GAAG;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAIrB;IACF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAqB;IACrD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiB;IAC/C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAY;IAE1C,IAAI,WAAW,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,SAAS,CAEvD;IAED,OAAO,CAAC,YAAY,CAAC,CAA4B;gBAG/C,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,gBAAgB,GAAG;QACzB,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;QAC/B,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC;QACzB,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAA;KACpC,EACD,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI;IAWrC;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAc/B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAM5B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAS/B;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAWjC;;;;;;;;;OASG;IACH,OAAO,CAAC,iBAAiB;IAgDzB;;;OAGG;IACH,OAAO,CAAC,YAAY;IAWpB;;OAEG;IACH,OAAO,CAAC,cAAc;IAYtB;;OAEG;IACH,OAAO,CAAC,aAAa;IAarB;;OAEG;IACH,OAAO,CAAC,WAAW;IAUnB;;OAEG;IACH,OAAO,CAAC,UAAU;IASlB;;;;OAIG;IACU,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IA8B9C;;OAEG;IACI,KAAK,IAAI,IAAI;CAGrB"}
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Cursor configuration for different table types
3
+ */
4
+ const EVENT_CURSOR_COLUMNS = [
5
+ { name: 'blockNumber', sortOrder: 'DESC' },
6
+ { name: 'transactionIndex', sortOrder: 'DESC' },
7
+ { name: 'logIndex', sortOrder: 'DESC' },
8
+ ];
9
+ /**
10
+ * A class for querying Circles RPC nodes with cursor-based pagination.
11
+ * Supports both event-based pagination (default) and custom cursor pagination (for view tables).
12
+ *
13
+ * @typeParam TRow The type of the rows returned by the query.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // Event-based pagination
18
+ * const query = new PagedQuery<GroupMembershipRow>(rpc.client, {
19
+ * namespace: 'V_CrcV2',
20
+ * table: 'GroupMemberships',
21
+ * sortOrder: 'DESC',
22
+ * columns: ['blockNumber', 'transactionIndex', 'logIndex', 'group', 'member'],
23
+ * filter: [{ Type: 'FilterPredicate', FilterType: 'Equals', Column: 'group', Value: '0x...' }],
24
+ * limit: 100
25
+ * });
26
+ *
27
+ * // Custom cursor pagination (for view tables)
28
+ * const viewQuery = new PagedQuery<GroupTokenHolderRow>(rpc.client, {
29
+ * namespace: 'V_CrcV2',
30
+ * table: 'GroupTokenHoldersBalance',
31
+ * sortOrder: 'ASC',
32
+ * columns: ['group', 'holder', 'totalBalance'],
33
+ * cursorColumns: [{ name: 'holder', sortOrder: 'ASC' }],
34
+ * filter: [{ Type: 'FilterPredicate', FilterType: 'Equals', Column: 'group', Value: '0x...' }],
35
+ * limit: 100
36
+ * });
37
+ * ```
38
+ */
39
+ export class PagedQuery {
40
+ params;
41
+ client;
42
+ rowTransformer;
43
+ cursorColumns;
44
+ orderColumns;
45
+ get currentPage() {
46
+ return this._currentPage;
47
+ }
48
+ _currentPage;
49
+ constructor(client, params, rowTransformer) {
50
+ this.client = client;
51
+ this.params = params;
52
+ this.rowTransformer = rowTransformer || params.rowTransformer;
53
+ this.orderColumns = params.orderColumns;
54
+ // Determine cursor columns based on table type
55
+ this.cursorColumns = params.cursorColumns || this.buildEventCursorColumns();
56
+ }
57
+ /**
58
+ * Builds cursor columns for event-based tables
59
+ */
60
+ buildEventCursorColumns() {
61
+ const columns = EVENT_CURSOR_COLUMNS.map(col => ({
62
+ ...col,
63
+ sortOrder: this.params.sortOrder
64
+ }));
65
+ // Add batchIndex for TransferBatch table
66
+ if (this.params.table === 'TransferBatch') {
67
+ columns.push({ name: 'batchIndex', sortOrder: this.params.sortOrder });
68
+ }
69
+ return columns;
70
+ }
71
+ /**
72
+ * Transforms a cursor value for use in query filters
73
+ */
74
+ transformCursorValue(value, transformer) {
75
+ if (transformer)
76
+ return transformer(value);
77
+ if (typeof value === 'bigint')
78
+ return value.toString();
79
+ return value;
80
+ }
81
+ /**
82
+ * Creates an equality predicate for a cursor column
83
+ */
84
+ createEqualityPredicate(column, value) {
85
+ return {
86
+ Type: 'FilterPredicate',
87
+ FilterType: 'Equals',
88
+ Column: column.name,
89
+ Value: this.transformCursorValue(value, column.toValue),
90
+ };
91
+ }
92
+ /**
93
+ * Creates a comparison predicate for a cursor column (> or <)
94
+ */
95
+ createComparisonPredicate(column, value) {
96
+ const filterType = column.sortOrder === 'ASC' ? 'GreaterThan' : 'LessThan';
97
+ return {
98
+ Type: 'FilterPredicate',
99
+ FilterType: filterType,
100
+ Column: column.name,
101
+ Value: this.transformCursorValue(value, column.toValue),
102
+ };
103
+ }
104
+ /**
105
+ * Builds cursor filter for pagination using composite cursor columns.
106
+ *
107
+ * Creates an OR conjunction of predicates for each cursor level:
108
+ * - First level: col1 > cursor.col1
109
+ * - Second level: col1 = cursor.col1 AND col2 > cursor.col2
110
+ * - Third level: col1 = cursor.col1 AND col2 = cursor.col2 AND col3 > cursor.col3
111
+ *
112
+ * This ensures correct pagination across all cursor columns.
113
+ */
114
+ buildCursorFilter(cursor) {
115
+ if (!cursor)
116
+ return [];
117
+ const orPredicates = [];
118
+ for (let level = 0; level < this.cursorColumns.length; level++) {
119
+ const currentColumn = this.cursorColumns[level];
120
+ const cursorValue = cursor[currentColumn.name];
121
+ if (cursorValue === undefined)
122
+ continue;
123
+ if (level === 0) {
124
+ // First level: simple comparison (col > value)
125
+ orPredicates.push(this.createComparisonPredicate(currentColumn, cursorValue));
126
+ }
127
+ else {
128
+ // Subsequent levels: equality for all previous + comparison for current
129
+ const andPredicates = [];
130
+ // Add equality predicates for all previous columns
131
+ for (let prevLevel = 0; prevLevel < level; prevLevel++) {
132
+ const prevColumn = this.cursorColumns[prevLevel];
133
+ const prevValue = cursor[prevColumn.name];
134
+ if (prevValue !== undefined) {
135
+ andPredicates.push(this.createEqualityPredicate(prevColumn, prevValue));
136
+ }
137
+ }
138
+ // Add comparison predicate for current column
139
+ andPredicates.push(this.createComparisonPredicate(currentColumn, cursorValue));
140
+ orPredicates.push({
141
+ Type: 'Conjunction',
142
+ ConjunctionType: 'And',
143
+ Predicates: andPredicates,
144
+ });
145
+ }
146
+ }
147
+ if (orPredicates.length === 0)
148
+ return [];
149
+ return [{
150
+ Type: 'Conjunction',
151
+ ConjunctionType: 'Or',
152
+ Predicates: orPredicates,
153
+ }];
154
+ }
155
+ /**
156
+ * Builds the order by clause.
157
+ * If orderColumns are provided, uses those. Otherwise builds from cursor columns.
158
+ */
159
+ buildOrderBy() {
160
+ if (this.orderColumns && this.orderColumns.length > 0) {
161
+ return this.orderColumns;
162
+ }
163
+ return this.cursorColumns.map(col => ({
164
+ Column: col.name,
165
+ SortOrder: col.sortOrder,
166
+ }));
167
+ }
168
+ /**
169
+ * Combines base filters with cursor filter
170
+ */
171
+ combineFilters(baseFilters, cursorFilter) {
172
+ if (!baseFilters?.length && !cursorFilter?.length)
173
+ return [];
174
+ if (!baseFilters?.length)
175
+ return cursorFilter || [];
176
+ if (!cursorFilter?.length)
177
+ return baseFilters;
178
+ return [{
179
+ Type: 'Conjunction',
180
+ ConjunctionType: 'And',
181
+ Predicates: [...baseFilters, ...cursorFilter],
182
+ }];
183
+ }
184
+ /**
185
+ * Converts query response rows to typed objects
186
+ */
187
+ rowsToObjects(response) {
188
+ const { columns, rows } = response;
189
+ return rows.map(row => {
190
+ const rowObj = {};
191
+ columns.forEach((col, index) => {
192
+ rowObj[col] = row[index];
193
+ });
194
+ return this.rowTransformer ? this.rowTransformer(rowObj) : rowObj;
195
+ });
196
+ }
197
+ /**
198
+ * Extracts cursor values from a row
199
+ */
200
+ rowToCursor(row) {
201
+ const cursor = {};
202
+ for (const column of this.cursorColumns) {
203
+ cursor[column.name] = row[column.name];
204
+ }
205
+ return cursor;
206
+ }
207
+ /**
208
+ * Gets first and last cursors from result set
209
+ */
210
+ getCursors(results) {
211
+ if (results.length === 0)
212
+ return {};
213
+ return {
214
+ first: this.rowToCursor(results[0]),
215
+ last: this.rowToCursor(results[results.length - 1]),
216
+ };
217
+ }
218
+ /**
219
+ * Queries the next page of results.
220
+ *
221
+ * @returns True if results were found, false otherwise
222
+ */
223
+ async queryNextPage() {
224
+ const cursorFilter = this.buildCursorFilter(this._currentPage?.lastCursor);
225
+ const combinedFilter = this.combineFilters(this.params.filter, cursorFilter);
226
+ const queryParams = {
227
+ Namespace: this.params.namespace,
228
+ Table: this.params.table,
229
+ Columns: this.params.columns,
230
+ Filter: combinedFilter,
231
+ Order: this.buildOrderBy(),
232
+ Limit: this.params.limit,
233
+ };
234
+ const response = await this.client.call('circles_query', [queryParams]);
235
+ const results = this.rowsToObjects(response);
236
+ const cursors = this.getCursors(results);
237
+ this._currentPage = {
238
+ limit: this.params.limit,
239
+ size: results.length,
240
+ firstCursor: cursors.first,
241
+ lastCursor: cursors.last,
242
+ sortOrder: this.params.sortOrder,
243
+ hasMore: results.length === this.params.limit,
244
+ results,
245
+ };
246
+ return results.length > 0;
247
+ }
248
+ /**
249
+ * Resets the query to start from the beginning
250
+ */
251
+ reset() {
252
+ this._currentPage = undefined;
253
+ }
254
+ }
package/dist/rpc.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { RpcClient } from './client';
2
+ import { PathfinderMethods, QueryMethods, TrustMethods, BalanceMethods, AvatarMethods, ProfileMethods, TokenMethods, InvitationMethods, TransactionMethods, GroupMethods } from './methods';
3
+ /**
4
+ * Main RPC class for Circles protocol RPC interactions
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * // Use default RPC endpoint
9
+ * const rpc = new CirclesRpc();
10
+ *
11
+ * // Use custom RPC endpoint
12
+ * const rpc = new CirclesRpc('https://rpc.circlesubi.network/');
13
+ *
14
+ * // Find a path
15
+ * const path = await rpc.pathfinder.findPath({
16
+ * Source: '0x749c930256b47049cb65adcd7c25e72d5de44b3b',
17
+ * Sink: '0xde374ece6fa50e781e81aac78e811b33d16912c7',
18
+ * TargetFlow: '99999999999999999999999999999999999'
19
+ * });
20
+ *
21
+ * // Query trust relations
22
+ * const trustRelations = await rpc.query.query({
23
+ * Namespace: 'V_CrcV2',
24
+ * Table: 'TrustRelations',
25
+ * Columns: [],
26
+ * Filter: [],
27
+ * Order: []
28
+ * });
29
+ *
30
+ * // Get profile
31
+ * const profile = await rpc.profile.getProfileByAddress('0xc3a1428c04c426cdf513c6fc8e09f55ddaf50cd7');
32
+ * ```
33
+ */
34
+ export declare class CirclesRpc {
35
+ readonly client: RpcClient;
36
+ readonly pathfinder: PathfinderMethods;
37
+ readonly query: QueryMethods;
38
+ readonly trust: TrustMethods;
39
+ readonly balance: BalanceMethods;
40
+ readonly avatar: AvatarMethods;
41
+ readonly profile: ProfileMethods;
42
+ readonly token: TokenMethods;
43
+ readonly invitation: InvitationMethods;
44
+ readonly transaction: TransactionMethods;
45
+ readonly group: GroupMethods;
46
+ /**
47
+ * Create a new CirclesRpc instance
48
+ *
49
+ * @param rpcUrl RPC URL to use (defaults to https://rpc.circlesubi.network/)
50
+ */
51
+ constructor(rpcUrl?: string);
52
+ /**
53
+ * Update the RPC URL
54
+ */
55
+ setRpcUrl(rpcUrl: string): void;
56
+ /**
57
+ * Get the current RPC URL
58
+ */
59
+ getRpcUrl(): string;
60
+ }
61
+ //# sourceMappingURL=rpc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../src/rpc.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,aAAa,EACb,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,YAAY,EACb,MAAM,WAAW,CAAC;AAEnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,UAAU;IACrB,SAAgB,MAAM,EAAE,SAAS,CAAC;IAClC,SAAgB,UAAU,EAAE,iBAAiB,CAAC;IAC9C,SAAgB,KAAK,EAAE,YAAY,CAAC;IACpC,SAAgB,KAAK,EAAE,YAAY,CAAC;IACpC,SAAgB,OAAO,EAAE,cAAc,CAAC;IACxC,SAAgB,MAAM,EAAE,aAAa,CAAC;IACtC,SAAgB,OAAO,EAAE,cAAc,CAAC;IACxC,SAAgB,KAAK,EAAE,YAAY,CAAC;IACpC,SAAgB,UAAU,EAAE,iBAAiB,CAAC;IAC9C,SAAgB,WAAW,EAAE,kBAAkB,CAAC;IAChD,SAAgB,KAAK,EAAE,YAAY,CAAC;IAEpC;;;;OAIG;gBACS,MAAM,GAAE,MAA0C;IAe9D;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B;;OAEG;IACH,SAAS,IAAI,MAAM;CAGpB"}