@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.
- package/README.md +169 -0
- package/dist/client.d.ts +58 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +194 -0
- package/dist/errors.d.ts +44 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +63 -0
- package/dist/events/index.d.ts +8 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +8 -0
- package/dist/events/observable.d.ts +23 -0
- package/dist/events/observable.d.ts.map +1 -0
- package/dist/events/observable.js +37 -0
- package/dist/events/parser.d.ts +10 -0
- package/dist/events/parser.d.ts.map +1 -0
- package/dist/events/parser.js +103 -0
- package/dist/events/types.d.ts +7 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/events/types.js +11 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2654 -0
- package/dist/methods/avatar.d.ts +51 -0
- package/dist/methods/avatar.d.ts.map +1 -0
- package/dist/methods/avatar.js +64 -0
- package/dist/methods/balance.d.ts +37 -0
- package/dist/methods/balance.d.ts.map +1 -0
- package/dist/methods/balance.js +50 -0
- package/dist/methods/group.d.ts +145 -0
- package/dist/methods/group.d.ts.map +1 -0
- package/dist/methods/group.js +380 -0
- package/dist/methods/index.d.ts +11 -0
- package/dist/methods/index.d.ts.map +1 -0
- package/dist/methods/index.js +10 -0
- package/dist/methods/invitation.d.ts +61 -0
- package/dist/methods/invitation.d.ts.map +1 -0
- package/dist/methods/invitation.js +230 -0
- package/dist/methods/pathfinder.d.ts +41 -0
- package/dist/methods/pathfinder.d.ts.map +1 -0
- package/dist/methods/pathfinder.js +53 -0
- package/dist/methods/profile.d.ts +109 -0
- package/dist/methods/profile.d.ts.map +1 -0
- package/dist/methods/profile.js +166 -0
- package/dist/methods/query.d.ts +79 -0
- package/dist/methods/query.d.ts.map +1 -0
- package/dist/methods/query.js +87 -0
- package/dist/methods/token.d.ts +61 -0
- package/dist/methods/token.d.ts.map +1 -0
- package/dist/methods/token.js +99 -0
- package/dist/methods/transaction.d.ts +41 -0
- package/dist/methods/transaction.d.ts.map +1 -0
- package/dist/methods/transaction.js +111 -0
- package/dist/methods/trust.d.ts +114 -0
- package/dist/methods/trust.d.ts.map +1 -0
- package/dist/methods/trust.js +245 -0
- package/dist/pagedQuery.d.ts +106 -0
- package/dist/pagedQuery.d.ts.map +1 -0
- package/dist/pagedQuery.js +254 -0
- package/dist/rpc.d.ts +61 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +76 -0
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +27 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +111 -0
- 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"}
|