@aboutcircles/sdk-invitations 0.1.25 → 0.1.26
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/dist/Distributions.d.ts +1 -1
- package/dist/Distributions.d.ts.map +1 -1
- package/dist/Distributions.js +1 -1
- package/dist/Invitations.d.ts +1 -1
- package/dist/Invitations.d.ts.map +1 -1
- package/dist/Invitations.js +1 -1
- package/dist/InviteFarm.d.ts +1 -1
- package/dist/InviteFarm.d.ts.map +1 -1
- package/dist/InviteFarm.js +2 -2
- package/dist/Referrals.d.ts +1 -1
- package/dist/Referrals.d.ts.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/package.json +1 -1
- package/dist/tests/findInvitePath.test.d.ts +0 -2
- package/dist/tests/findInvitePath.test.d.ts.map +0 -1
- package/dist/tests/findInvitePath.test.js +0 -41
- package/dist/tests/fuzz/invite-checker.d.ts +0 -2
- package/dist/tests/fuzz/invite-checker.d.ts.map +0 -1
- package/dist/tests/fuzz/invite-checker.js +0 -117
- package/dist/tests/fuzz/networkFuzz.d.ts +0 -6
- package/dist/tests/fuzz/networkFuzz.d.ts.map +0 -1
- package/dist/tests/fuzz/networkFuzz.js +0 -290
- package/dist/tests/generateInvite.test.d.ts +0 -2
- package/dist/tests/generateInvite.test.d.ts.map +0 -1
- package/dist/tests/generateInvite.test.js +0 -55
- package/dist/tests/getRealInviters.test.d.ts +0 -2
- package/dist/tests/getRealInviters.test.d.ts.map +0 -1
- package/dist/tests/getRealInviters.test.js +0 -77
- package/dist/tests/helpers/mockFetch.d.ts +0 -7
- package/dist/tests/helpers/mockFetch.d.ts.map +0 -1
- package/dist/tests/helpers/mockFetch.js +0 -282
- package/dist/tests/helpers/scenario.d.ts +0 -77
- package/dist/tests/helpers/scenario.d.ts.map +0 -1
- package/dist/tests/helpers/scenario.js +0 -104
- package/dist/tests/helpers/scenarios.d.ts +0 -40
- package/dist/tests/helpers/scenarios.d.ts.map +0 -1
- package/dist/tests/helpers/scenarios.js +0 -111
|
@@ -1,282 +0,0 @@
|
|
|
1
|
-
// ============================================================================
|
|
2
|
-
// ABI encoding helpers (minimal, no deps on viem)
|
|
3
|
-
// ============================================================================
|
|
4
|
-
/** Pad hex to 32-byte word */
|
|
5
|
-
const pad32 = (hex) => hex.padStart(64, '0');
|
|
6
|
-
/** Encode a single bool as a 32-byte ABI word */
|
|
7
|
-
function encodeBool(value) {
|
|
8
|
-
return pad32(value ? '1' : '0');
|
|
9
|
-
}
|
|
10
|
-
/** Decode an address from a 32-byte ABI word (last 20 bytes) */
|
|
11
|
-
function decodeAddress(word) {
|
|
12
|
-
return ('0x' + word.slice(-40).toLowerCase());
|
|
13
|
-
}
|
|
14
|
-
// ============================================================================
|
|
15
|
-
// Function selectors (keccak256 of signature, first 4 bytes)
|
|
16
|
-
// These are the standard Ethereum function selectors.
|
|
17
|
-
// Computed offline: cast sig "functionName(types)"
|
|
18
|
-
// ============================================================================
|
|
19
|
-
const SELECTORS = {
|
|
20
|
-
// HubV2
|
|
21
|
-
isHuman: '0xf72c436f', // isHuman(address)
|
|
22
|
-
isTrusted: '0x6713e230', // isTrusted(address,address)
|
|
23
|
-
// Safe
|
|
24
|
-
isModuleEnabled: '0x2d9ad53d', // isModuleEnabled(address)
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* Extract function selector from calldata (first 4 bytes = 8 hex chars)
|
|
28
|
-
*/
|
|
29
|
-
function getSelector(data) {
|
|
30
|
-
return data.slice(0, 10).toLowerCase(); // "0x" + 8 hex chars
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Decode `data` as eth_call calldata for `isHuman(address)` — returns the address arg
|
|
34
|
-
*/
|
|
35
|
-
function decodeAddress1(data) {
|
|
36
|
-
// selector (4 bytes) + address padded to 32 bytes
|
|
37
|
-
const word = data.slice(10, 10 + 64);
|
|
38
|
-
return decodeAddress(word);
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Decode `data` as eth_call calldata for `isTrusted(address,address)` — returns [truster, trustee]
|
|
42
|
-
*/
|
|
43
|
-
function decodeAddress2(data) {
|
|
44
|
-
const word1 = data.slice(10, 10 + 64);
|
|
45
|
-
const word2 = data.slice(10 + 64, 10 + 128);
|
|
46
|
-
return [decodeAddress(word1), decodeAddress(word2)];
|
|
47
|
-
}
|
|
48
|
-
// ============================================================================
|
|
49
|
-
// circles_query response builder
|
|
50
|
-
// ============================================================================
|
|
51
|
-
/**
|
|
52
|
-
* Build a circles_query response for TrustRelations table.
|
|
53
|
-
*
|
|
54
|
-
* The TrustMethods.getAggregatedTrustRelations() calls circles_query once per
|
|
55
|
-
* avatar, paginating until `hasMore = false`. We return all matching rows in
|
|
56
|
-
* one page so pagination stops immediately.
|
|
57
|
-
*/
|
|
58
|
-
function buildTrustQueryResponse(scenario, avatar) {
|
|
59
|
-
const lowerAvatar = avatar.toLowerCase();
|
|
60
|
-
const rows = scenario.trusts
|
|
61
|
-
.filter(t => t.truster.toLowerCase() === lowerAvatar ||
|
|
62
|
-
t.trustee.toLowerCase() === lowerAvatar)
|
|
63
|
-
.map(t => [
|
|
64
|
-
0, // blockNumber
|
|
65
|
-
0, // timestamp
|
|
66
|
-
0, // transactionIndex
|
|
67
|
-
0, // logIndex
|
|
68
|
-
'0x0000', // transactionHash
|
|
69
|
-
2, // version
|
|
70
|
-
t.trustee.toLowerCase(),
|
|
71
|
-
t.truster.toLowerCase(),
|
|
72
|
-
0, // expiryTime
|
|
73
|
-
]);
|
|
74
|
-
return {
|
|
75
|
-
columns: [
|
|
76
|
-
'blockNumber', 'timestamp', 'transactionIndex', 'logIndex',
|
|
77
|
-
'transactionHash', 'version', 'trustee', 'truster', 'expiryTime'
|
|
78
|
-
],
|
|
79
|
-
rows,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Parse which avatar address is being queried from a circles_query params object.
|
|
84
|
-
* Looks for FilterPredicate on trustee or truster column with Equals filter.
|
|
85
|
-
*/
|
|
86
|
-
function extractAvatarFromTrustQuery(params) {
|
|
87
|
-
const filterList = Array.isArray(params?.Filter) ? params.Filter : [];
|
|
88
|
-
function findAvatarInFilter(filter) {
|
|
89
|
-
if (!filter)
|
|
90
|
-
return null;
|
|
91
|
-
if (filter.Type === 'FilterPredicate' && filter.FilterType === 'Equals') {
|
|
92
|
-
const col = filter.Column;
|
|
93
|
-
if (col === 'trustee' || col === 'truster') {
|
|
94
|
-
return typeof filter.Value === 'string' ? filter.Value : null;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (filter.Type === 'Conjunction' && Array.isArray(filter.Predicates)) {
|
|
98
|
-
for (const pred of filter.Predicates) {
|
|
99
|
-
const found = findAvatarInFilter(pred);
|
|
100
|
-
if (found)
|
|
101
|
-
return found;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
for (const f of filterList) {
|
|
107
|
-
const found = findAvatarInFilter(f);
|
|
108
|
-
if (found)
|
|
109
|
-
return found;
|
|
110
|
-
}
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
// ============================================================================
|
|
114
|
-
// Main mock fetch factory
|
|
115
|
-
// ============================================================================
|
|
116
|
-
/**
|
|
117
|
-
* Creates a mock fetch function that intercepts all RPC calls used by Invitations.
|
|
118
|
-
*
|
|
119
|
-
* Intercepts:
|
|
120
|
-
* - eth_call → isHuman, isModuleEnabled, isTrusted
|
|
121
|
-
* - circles_query → TrustRelations (for getTrusts/getTrustedBy/getMutualTrusts)
|
|
122
|
-
* - circlesV2_findPath → pathfinder simulation
|
|
123
|
-
* - circles_getTokenInfoBatch → token info (identity mapping for human tokens)
|
|
124
|
-
*/
|
|
125
|
-
const REAL_PATHFINDER_URL = 'https://rpc.aboutcircles.com/';
|
|
126
|
-
const nativeFetch = globalThis.fetch.bind(globalThis);
|
|
127
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
128
|
-
export function createMockFetch(scenario) {
|
|
129
|
-
return async (input, _init) => {
|
|
130
|
-
const url = typeof input === 'string' ? input : input.toString();
|
|
131
|
-
const body = JSON.parse(_init?.body ?? '{}');
|
|
132
|
-
const { method, params, id } = body;
|
|
133
|
-
function ok(result) {
|
|
134
|
-
return new Response(JSON.stringify({ jsonrpc: '2.0', id, result }), { status: 200, headers: { 'Content-Type': 'application/json' } });
|
|
135
|
-
}
|
|
136
|
-
// ---- eth_call ----
|
|
137
|
-
if (method === 'eth_call') {
|
|
138
|
-
const callParams = params[0];
|
|
139
|
-
const to = (callParams.to ?? '').toLowerCase();
|
|
140
|
-
const data = (callParams.data ?? '').toLowerCase();
|
|
141
|
-
const selector = getSelector(data);
|
|
142
|
-
// isHuman(address) → HubV2
|
|
143
|
-
if (selector === SELECTORS.isHuman) {
|
|
144
|
-
const addr = decodeAddress1(data);
|
|
145
|
-
const entry = scenario.avatars.find(a => a.address.toLowerCase() === addr.toLowerCase());
|
|
146
|
-
const isHuman = entry?.isHuman ?? false;
|
|
147
|
-
return ok('0x' + encodeBool(isHuman));
|
|
148
|
-
}
|
|
149
|
-
// isTrusted(address,address) → HubV2
|
|
150
|
-
if (selector === SELECTORS.isTrusted) {
|
|
151
|
-
const [truster, trustee] = decodeAddress2(data);
|
|
152
|
-
const key = `${truster.toLowerCase()}:${trustee.toLowerCase()}`;
|
|
153
|
-
const trusted = scenario.isTrusted[key] ?? false;
|
|
154
|
-
return ok('0x' + encodeBool(trusted));
|
|
155
|
-
}
|
|
156
|
-
// isModuleEnabled(address) → Safe (the Safe address is `to`)
|
|
157
|
-
if (selector === SELECTORS.isModuleEnabled) {
|
|
158
|
-
const enabled = scenario.moduleEnabled[to] ?? false;
|
|
159
|
-
return ok('0x' + encodeBool(enabled));
|
|
160
|
-
}
|
|
161
|
-
// Unknown eth_call — return false/zero
|
|
162
|
-
return ok('0x' + pad32('0'));
|
|
163
|
-
}
|
|
164
|
-
// ---- circles_query (trust relations pagination) ----
|
|
165
|
-
if (method === 'circles_query') {
|
|
166
|
-
const queryParams = params[0];
|
|
167
|
-
const avatar = extractAvatarFromTrustQuery(queryParams);
|
|
168
|
-
if (!avatar) {
|
|
169
|
-
return ok({ columns: [], rows: [] });
|
|
170
|
-
}
|
|
171
|
-
const response = buildTrustQueryResponse(scenario, avatar);
|
|
172
|
-
return ok(response);
|
|
173
|
-
}
|
|
174
|
-
// ---- circlesV2_findPath → forward to real pathfinder with scenario injected ----
|
|
175
|
-
if (method === 'circlesV2_findPath') {
|
|
176
|
-
const p = params[0];
|
|
177
|
-
// Inject scenario balances and trusts as simulated params so the real pathfinder
|
|
178
|
-
// operates on the test graph without any on-chain state.
|
|
179
|
-
const scenarioBalances = scenario.balances.map(b => ({
|
|
180
|
-
Holder: b.holder,
|
|
181
|
-
Token: b.tokenAddress,
|
|
182
|
-
Amount: b.amount.toString(),
|
|
183
|
-
IsWrapped: b.isWrapped ?? false,
|
|
184
|
-
IsStatic: true,
|
|
185
|
-
}));
|
|
186
|
-
const scenarioTrusts = scenario.trusts.map(t => ({
|
|
187
|
-
Truster: t.truster,
|
|
188
|
-
Trustee: t.trustee,
|
|
189
|
-
}));
|
|
190
|
-
// Merge caller-provided simulated params on top of scenario params
|
|
191
|
-
const mergedBalances = [
|
|
192
|
-
...scenarioBalances,
|
|
193
|
-
...(p.SimulatedBalances ?? []),
|
|
194
|
-
];
|
|
195
|
-
const mergedTrusts = [
|
|
196
|
-
...scenarioTrusts,
|
|
197
|
-
...(p.SimulatedTrusts ?? []),
|
|
198
|
-
];
|
|
199
|
-
const forwardedParams = { ...p, SimulatedBalances: mergedBalances, SimulatedTrusts: mergedTrusts };
|
|
200
|
-
const realResponse = await nativeFetch(REAL_PATHFINDER_URL, {
|
|
201
|
-
method: 'POST',
|
|
202
|
-
headers: { 'Content-Type': 'application/json' },
|
|
203
|
-
body: JSON.stringify({ jsonrpc: '2.0', id, method, params: [forwardedParams] }),
|
|
204
|
-
});
|
|
205
|
-
// The real pathfinder may return 500 for unknown/test addresses with no on-chain state.
|
|
206
|
-
// Treat this as a no-path result rather than a hard error.
|
|
207
|
-
if (!realResponse.ok) {
|
|
208
|
-
return ok({ maxFlow: '0', transfers: [] });
|
|
209
|
-
}
|
|
210
|
-
const json = await realResponse.json();
|
|
211
|
-
if (json.error) {
|
|
212
|
-
return ok({ maxFlow: '0', transfers: [] });
|
|
213
|
-
}
|
|
214
|
-
return new Response(JSON.stringify(json), {
|
|
215
|
-
status: 200,
|
|
216
|
-
headers: { 'Content-Type': 'application/json' },
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
// ---- circles_getTokenInfoBatch ----
|
|
220
|
-
if (method === 'circles_getTokenInfoBatch') {
|
|
221
|
-
const addresses = params[0] ?? [];
|
|
222
|
-
// Build a tokenAddress → tokenOwner map from scenario balances.
|
|
223
|
-
// For plain human ERC1155 tokens tokenAddress === tokenOwner (identity), but
|
|
224
|
-
// wrapped ERC20 tokens have a distinct tokenAddress pointing to the wrapper contract.
|
|
225
|
-
const tokenOwnerMap = new Map();
|
|
226
|
-
for (const b of scenario.balances) {
|
|
227
|
-
tokenOwnerMap.set(b.tokenAddress.toLowerCase(), b.tokenOwner.toLowerCase());
|
|
228
|
-
}
|
|
229
|
-
const tokenInfos = addresses.map(addr => {
|
|
230
|
-
const laddr = addr.toLowerCase();
|
|
231
|
-
const tokenOwner = tokenOwnerMap.get(laddr) ?? laddr; // fall back to identity
|
|
232
|
-
const isWrapped = tokenOwner !== laddr;
|
|
233
|
-
return {
|
|
234
|
-
tokenAddress: laddr,
|
|
235
|
-
tokenOwner,
|
|
236
|
-
type: isWrapped ? 'CrcV2_ERC20WrapperDeployed_Demurraged' : 'CrcV2_RegisterHuman',
|
|
237
|
-
tokenType: isWrapped ? 'CrcV2_ERC20WrapperDeployed_Demurraged' : 'CrcV2_RegisterHuman',
|
|
238
|
-
isWrapped,
|
|
239
|
-
isInflationary: false,
|
|
240
|
-
version: 2,
|
|
241
|
-
};
|
|
242
|
-
});
|
|
243
|
-
return ok(tokenInfos);
|
|
244
|
-
}
|
|
245
|
-
// ---- circles_getAvatarInfoBatch ----
|
|
246
|
-
if (method === 'circles_getAvatarInfoBatch') {
|
|
247
|
-
const addresses = params[0] ?? [];
|
|
248
|
-
const infos = addresses.map(addr => {
|
|
249
|
-
const entry = scenario.avatars.find(a => a.address.toLowerCase() === addr.toLowerCase());
|
|
250
|
-
return {
|
|
251
|
-
avatar: addr.toLowerCase(),
|
|
252
|
-
type: entry?.isHuman ? 'CrcV2_RegisterHuman' : 'CrcV2_RegisterOrganization',
|
|
253
|
-
isHuman: entry?.isHuman ?? false,
|
|
254
|
-
version: 2,
|
|
255
|
-
};
|
|
256
|
-
});
|
|
257
|
-
return ok(infos);
|
|
258
|
-
}
|
|
259
|
-
// Fallback for referrals service or unknown calls
|
|
260
|
-
if (url.includes('/store') || url.includes('/list/')) {
|
|
261
|
-
return new Response(JSON.stringify({ success: true }), {
|
|
262
|
-
status: 200,
|
|
263
|
-
headers: { 'Content-Type': 'application/json' },
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
// Unknown method — return empty result
|
|
267
|
-
return ok(null);
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Install mock fetch for the duration of `fn`, then restore original.
|
|
272
|
-
*/
|
|
273
|
-
export async function withMockFetch(scenario, fn) {
|
|
274
|
-
const original = globalThis.fetch;
|
|
275
|
-
globalThis.fetch = createMockFetch(scenario);
|
|
276
|
-
try {
|
|
277
|
-
return await fn();
|
|
278
|
-
}
|
|
279
|
-
finally {
|
|
280
|
-
globalThis.fetch = original;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type { Address } from '@aboutcircles/sdk-types';
|
|
2
|
-
import type { CirclesConfig } from '@aboutcircles/sdk-types';
|
|
3
|
-
/**
|
|
4
|
-
* Test addresses (zero-padded)
|
|
5
|
-
*/
|
|
6
|
-
export declare const INVITER: Address;
|
|
7
|
-
export declare const PROXY_A: Address;
|
|
8
|
-
export declare const PROXY_B: Address;
|
|
9
|
-
export declare const INVITEE: Address;
|
|
10
|
-
export declare const INV_MODULE: Address;
|
|
11
|
-
export declare const INV_FARM: Address;
|
|
12
|
-
export declare const REF_MODULE: Address;
|
|
13
|
-
export declare const HUB_V2: Address;
|
|
14
|
-
export declare const GNOSIS_GROUP: Address;
|
|
15
|
-
export declare const TEST_RPC_URL = "http://localhost:9999/rpc";
|
|
16
|
-
/**
|
|
17
|
-
* Minimal CirclesConfig for tests
|
|
18
|
-
*/
|
|
19
|
-
export declare const testConfig: CirclesConfig;
|
|
20
|
-
export interface TrustEntry {
|
|
21
|
-
truster: Address;
|
|
22
|
-
trustee: Address;
|
|
23
|
-
}
|
|
24
|
-
export interface TokenBalance {
|
|
25
|
-
holder: Address;
|
|
26
|
-
/** tokenOwner = the human avatar that issued the token */
|
|
27
|
-
tokenOwner: Address;
|
|
28
|
-
/** tokenAddress = the ERC1155/ERC20 token address (same as tokenOwner for unwrapped human tokens) */
|
|
29
|
-
tokenAddress: Address;
|
|
30
|
-
amount: bigint;
|
|
31
|
-
isWrapped?: boolean;
|
|
32
|
-
}
|
|
33
|
-
export interface AvatarEntry {
|
|
34
|
-
address: Address;
|
|
35
|
-
isHuman: boolean;
|
|
36
|
-
}
|
|
37
|
-
export interface Scenario {
|
|
38
|
-
/** Directed trust edges: truster → trustee */
|
|
39
|
-
trusts: TrustEntry[];
|
|
40
|
-
/** Token balances known to pathfinder */
|
|
41
|
-
balances: TokenBalance[];
|
|
42
|
-
/** Avatar info (isHuman flag per address) */
|
|
43
|
-
avatars: AvatarEntry[];
|
|
44
|
-
/** Safe module enablement: safeAddr → isEnabled */
|
|
45
|
-
moduleEnabled: Record<string, boolean>;
|
|
46
|
-
/** isTrusted results: `${truster}:${trustee}` → bool */
|
|
47
|
-
isTrusted: Record<string, boolean>;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Builder for test scenarios.
|
|
51
|
-
*
|
|
52
|
-
* Keeps all state that the mock fetch handler needs to answer
|
|
53
|
-
* circles_query, circlesV2_findPath, circles_getTokenInfoBatch,
|
|
54
|
-
* eth_call (isHuman, isModuleEnabled, isTrusted) calls.
|
|
55
|
-
*/
|
|
56
|
-
export declare class ScenarioBuilder {
|
|
57
|
-
private scenario;
|
|
58
|
-
trust(truster: Address, trustee: Address): this;
|
|
59
|
-
balance(holder: Address, tokenOwner: Address, amount: bigint, isWrapped?: boolean): this;
|
|
60
|
-
avatar(address: Address, isHuman: boolean): this;
|
|
61
|
-
enableModule(safeAddr: Address, enabled: boolean): this;
|
|
62
|
-
setIsTrusted(truster: Address, trustee: Address, value: boolean): this;
|
|
63
|
-
build(): Scenario;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Returns the set of addresses that `avatar` trusts (outgoing edges).
|
|
67
|
-
*/
|
|
68
|
-
export declare function getTrustsFor(scenario: Scenario, avatar: Address): Address[];
|
|
69
|
-
/**
|
|
70
|
-
* Returns addresses that trust `avatar` (incoming edges).
|
|
71
|
-
*/
|
|
72
|
-
export declare function getTrustedByFor(scenario: Scenario, avatar: Address): Address[];
|
|
73
|
-
/**
|
|
74
|
-
* Returns mutual trust addresses for `avatar`.
|
|
75
|
-
*/
|
|
76
|
-
export declare function getMutualTrustsFor(scenario: Scenario, avatar: Address): Address[];
|
|
77
|
-
//# sourceMappingURL=scenario.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scenario.d.ts","sourceRoot":"","sources":["../../../src/tests/helpers/scenario.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D;;GAEG;AACH,eAAO,MAAM,OAAO,EAA0D,OAAO,CAAC;AACtF,eAAO,MAAM,OAAO,EAA0D,OAAO,CAAC;AACtF,eAAO,MAAM,OAAO,EAA0D,OAAO,CAAC;AACtF,eAAO,MAAM,OAAO,EAA0D,OAAO,CAAC;AACtF,eAAO,MAAM,UAAU,EAAuD,OAAO,CAAC;AACtF,eAAO,MAAM,QAAQ,EAAyD,OAAO,CAAC;AACtF,eAAO,MAAM,UAAU,EAAuD,OAAO,CAAC;AACtF,eAAO,MAAM,MAAM,EAA2D,OAAO,CAAC;AACtF,eAAO,MAAM,YAAY,EAAqD,OAAO,CAAC;AAEtF,eAAO,MAAM,YAAY,8BAA8B,CAAC;AAExD;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,aAgBxB,CAAC;AAEF,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAC;IAChB,0DAA0D;IAC1D,UAAU,EAAE,OAAO,CAAC;IACpB,qGAAqG;IACrG,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,8CAA8C;IAC9C,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,yCAAyC;IACzC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,6CAA6C;IAC7C,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,mDAAmD;IACnD,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;;;;;GAMG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAMd;IAEF,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAK/C,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,IAAI;IAWtF,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAKhD,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI;IAKvD,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAKtE,KAAK,IAAI,QAAQ;CAGlB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,EAAE,CAK3E;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,EAAE,CAK9E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,EAAE,CAIjF"}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test addresses (zero-padded)
|
|
3
|
-
*/
|
|
4
|
-
export const INVITER = '0x0000000000000000000000000000000000000001';
|
|
5
|
-
export const PROXY_A = '0x0000000000000000000000000000000000000002';
|
|
6
|
-
export const PROXY_B = '0x0000000000000000000000000000000000000003';
|
|
7
|
-
export const INVITEE = '0x0000000000000000000000000000000000000004';
|
|
8
|
-
export const INV_MODULE = '0x0000000000000000000000000000000000000010';
|
|
9
|
-
export const INV_FARM = '0x0000000000000000000000000000000000000011';
|
|
10
|
-
export const REF_MODULE = '0x0000000000000000000000000000000000000012';
|
|
11
|
-
export const HUB_V2 = '0x0000000000000000000000000000000000000020';
|
|
12
|
-
export const GNOSIS_GROUP = '0x0000000000000000000000000000000000000030';
|
|
13
|
-
export const TEST_RPC_URL = 'http://localhost:9999/rpc';
|
|
14
|
-
/**
|
|
15
|
-
* Minimal CirclesConfig for tests
|
|
16
|
-
*/
|
|
17
|
-
export const testConfig = {
|
|
18
|
-
circlesRpcUrl: TEST_RPC_URL,
|
|
19
|
-
pathfinderUrl: 'https://rpc.aboutcircles.com/',
|
|
20
|
-
profileServiceUrl: 'http://localhost:9999/profile',
|
|
21
|
-
referralsServiceUrl: 'http://localhost:9999/referrals',
|
|
22
|
-
v1HubAddress: '0x0000000000000000000000000000000000000000',
|
|
23
|
-
v2HubAddress: HUB_V2,
|
|
24
|
-
nameRegistryAddress: '0x0000000000000000000000000000000000000000',
|
|
25
|
-
baseGroupMintPolicy: '0x0000000000000000000000000000000000000000',
|
|
26
|
-
standardTreasury: '0x0000000000000000000000000000000000000000',
|
|
27
|
-
coreMembersGroupDeployer: '0x0000000000000000000000000000000000000000',
|
|
28
|
-
baseGroupFactoryAddress: '0x0000000000000000000000000000000000000000',
|
|
29
|
-
liftERC20Address: '0x0000000000000000000000000000000000000000',
|
|
30
|
-
invitationFarmAddress: INV_FARM,
|
|
31
|
-
referralsModuleAddress: REF_MODULE,
|
|
32
|
-
invitationModuleAddress: INV_MODULE,
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* Builder for test scenarios.
|
|
36
|
-
*
|
|
37
|
-
* Keeps all state that the mock fetch handler needs to answer
|
|
38
|
-
* circles_query, circlesV2_findPath, circles_getTokenInfoBatch,
|
|
39
|
-
* eth_call (isHuman, isModuleEnabled, isTrusted) calls.
|
|
40
|
-
*/
|
|
41
|
-
export class ScenarioBuilder {
|
|
42
|
-
scenario = {
|
|
43
|
-
trusts: [],
|
|
44
|
-
balances: [],
|
|
45
|
-
avatars: [],
|
|
46
|
-
moduleEnabled: {},
|
|
47
|
-
isTrusted: {},
|
|
48
|
-
};
|
|
49
|
-
trust(truster, trustee) {
|
|
50
|
-
this.scenario.trusts.push({ truster, trustee });
|
|
51
|
-
return this;
|
|
52
|
-
}
|
|
53
|
-
balance(holder, tokenOwner, amount, isWrapped = false) {
|
|
54
|
-
this.scenario.balances.push({
|
|
55
|
-
holder,
|
|
56
|
-
tokenOwner,
|
|
57
|
-
tokenAddress: tokenOwner,
|
|
58
|
-
amount,
|
|
59
|
-
isWrapped,
|
|
60
|
-
});
|
|
61
|
-
return this;
|
|
62
|
-
}
|
|
63
|
-
avatar(address, isHuman) {
|
|
64
|
-
this.scenario.avatars.push({ address, isHuman });
|
|
65
|
-
return this;
|
|
66
|
-
}
|
|
67
|
-
enableModule(safeAddr, enabled) {
|
|
68
|
-
this.scenario.moduleEnabled[safeAddr.toLowerCase()] = enabled;
|
|
69
|
-
return this;
|
|
70
|
-
}
|
|
71
|
-
setIsTrusted(truster, trustee, value) {
|
|
72
|
-
this.scenario.isTrusted[`${truster.toLowerCase()}:${trustee.toLowerCase()}`] = value;
|
|
73
|
-
return this;
|
|
74
|
-
}
|
|
75
|
-
build() {
|
|
76
|
-
return { ...this.scenario };
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Returns the set of addresses that `avatar` trusts (outgoing edges).
|
|
81
|
-
*/
|
|
82
|
-
export function getTrustsFor(scenario, avatar) {
|
|
83
|
-
const lower = avatar.toLowerCase();
|
|
84
|
-
return scenario.trusts
|
|
85
|
-
.filter(t => t.truster.toLowerCase() === lower)
|
|
86
|
-
.map(t => t.trustee.toLowerCase());
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Returns addresses that trust `avatar` (incoming edges).
|
|
90
|
-
*/
|
|
91
|
-
export function getTrustedByFor(scenario, avatar) {
|
|
92
|
-
const lower = avatar.toLowerCase();
|
|
93
|
-
return scenario.trusts
|
|
94
|
-
.filter(t => t.trustee.toLowerCase() === lower)
|
|
95
|
-
.map(t => t.truster.toLowerCase());
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Returns mutual trust addresses for `avatar`.
|
|
99
|
-
*/
|
|
100
|
-
export function getMutualTrustsFor(scenario, avatar) {
|
|
101
|
-
const outgoing = new Set(getTrustsFor(scenario, avatar));
|
|
102
|
-
const incoming = new Set(getTrustedByFor(scenario, avatar));
|
|
103
|
-
return [...outgoing].filter(a => incoming.has(a)).map(a => a.toLowerCase());
|
|
104
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import type { Scenario } from './scenario';
|
|
2
|
-
export type { Scenario };
|
|
3
|
-
/**
|
|
4
|
-
* Inviter holds their own token with exactly INVITATION_FEE.
|
|
5
|
-
* Module is enabled and already trusts INVITER.
|
|
6
|
-
* generateInvite returns only operateFlowMatrix (no setup txs).
|
|
7
|
-
*/
|
|
8
|
-
export declare const personalCrcInvite: Scenario;
|
|
9
|
-
/**
|
|
10
|
-
* Inviter holds PROXY_A's token. PROXY_A trusts INVITER, module trusts PROXY_A.
|
|
11
|
-
* getRealInviters returns PROXY_A; generateInvite succeeds.
|
|
12
|
-
*/
|
|
13
|
-
export declare const proxyInviterCrcInvite: Scenario;
|
|
14
|
-
/**
|
|
15
|
-
* Module is NOT enabled on INVITER's Safe.
|
|
16
|
-
* generateInvite / ensureInviterSetup returns [enableModule, trustInviter, ...].
|
|
17
|
-
*/
|
|
18
|
-
export declare const moduleNotEnabledInvite: Scenario;
|
|
19
|
-
/**
|
|
20
|
-
* INVITEE is already a registered human.
|
|
21
|
-
* generateInvite throws INVITATION_INVITEE_ALREADY_REGISTERED.
|
|
22
|
-
*/
|
|
23
|
-
export declare const alreadyRegisteredInvitee: Scenario;
|
|
24
|
-
/**
|
|
25
|
-
* INVITER holds own token (2 invites) and PROXY_B's token (1 invite).
|
|
26
|
-
* getRealInviters returns [{INVITER, 2}, {PROXY_B, 1}] — inviter first.
|
|
27
|
-
*/
|
|
28
|
-
export declare const multipleProxyInviters: Scenario;
|
|
29
|
-
/**
|
|
30
|
-
* INVITER holds only half of INVITATION_FEE for PROXY_A's token.
|
|
31
|
-
* findInvitePath throws INVITATION_INSUFFICIENT_BALANCE.
|
|
32
|
-
*/
|
|
33
|
-
export declare const insufficientBalance: Scenario;
|
|
34
|
-
/**
|
|
35
|
-
* GNOSIS_GROUP_ADDRESS trusts PROXY_A (set1), which excludes PROXY_A as a real inviter.
|
|
36
|
-
* getRealInviters returns [].
|
|
37
|
-
*/
|
|
38
|
-
export declare const gnosisGroupExcluded: Scenario;
|
|
39
|
-
export declare const descriptions: Record<string, string>;
|
|
40
|
-
//# sourceMappingURL=scenarios.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scenarios.d.ts","sourceRoot":"","sources":["../../../src/tests/helpers/scenarios.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAK3C,YAAY,EAAE,QAAQ,EAAE,CAAC;AAEzB;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAOtB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,QAU1B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAS3B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,QAO7B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,QAW1B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,QASxB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,QAUxB,CAAC;AAEX,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQ/C,CAAC"}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { INVITATION_FEE, GNOSIS_GROUP_ADDRESS } from '@aboutcircles/sdk-utils';
|
|
2
|
-
import { ScenarioBuilder } from './scenario';
|
|
3
|
-
import { INVITER, INVITEE, PROXY_A, PROXY_B, INV_MODULE, } from './scenario';
|
|
4
|
-
/**
|
|
5
|
-
* Inviter holds their own token with exactly INVITATION_FEE.
|
|
6
|
-
* Module is enabled and already trusts INVITER.
|
|
7
|
-
* generateInvite returns only operateFlowMatrix (no setup txs).
|
|
8
|
-
*/
|
|
9
|
-
export const personalCrcInvite = new ScenarioBuilder()
|
|
10
|
-
.avatar(INVITER, true)
|
|
11
|
-
.avatar(INVITEE, false)
|
|
12
|
-
.enableModule(INVITER, true)
|
|
13
|
-
.setIsTrusted(INV_MODULE, INVITER, true)
|
|
14
|
-
.trust(INV_MODULE, INVITER)
|
|
15
|
-
.balance(INVITER, INVITER, INVITATION_FEE)
|
|
16
|
-
.build();
|
|
17
|
-
/**
|
|
18
|
-
* Inviter holds PROXY_A's token. PROXY_A trusts INVITER, module trusts PROXY_A.
|
|
19
|
-
* getRealInviters returns PROXY_A; generateInvite succeeds.
|
|
20
|
-
*/
|
|
21
|
-
export const proxyInviterCrcInvite = new ScenarioBuilder()
|
|
22
|
-
.avatar(INVITER, true)
|
|
23
|
-
.avatar(PROXY_A, true)
|
|
24
|
-
.avatar(INVITEE, false)
|
|
25
|
-
.enableModule(INVITER, true)
|
|
26
|
-
.setIsTrusted(INV_MODULE, INVITER, true)
|
|
27
|
-
.trust(PROXY_A, INVITER)
|
|
28
|
-
.trust(INV_MODULE, PROXY_A)
|
|
29
|
-
.trust(INV_MODULE, INVITER)
|
|
30
|
-
.balance(INVITER, PROXY_A, INVITATION_FEE)
|
|
31
|
-
.build();
|
|
32
|
-
/**
|
|
33
|
-
* Module is NOT enabled on INVITER's Safe.
|
|
34
|
-
* generateInvite / ensureInviterSetup returns [enableModule, trustInviter, ...].
|
|
35
|
-
*/
|
|
36
|
-
export const moduleNotEnabledInvite = new ScenarioBuilder()
|
|
37
|
-
.avatar(INVITER, true)
|
|
38
|
-
.avatar(PROXY_A, true)
|
|
39
|
-
.avatar(INVITEE, false)
|
|
40
|
-
.enableModule(INVITER, false)
|
|
41
|
-
.setIsTrusted(INV_MODULE, INVITER, false)
|
|
42
|
-
.trust(PROXY_A, INVITER)
|
|
43
|
-
.trust(INV_MODULE, PROXY_A)
|
|
44
|
-
.balance(INVITER, PROXY_A, INVITATION_FEE)
|
|
45
|
-
.build();
|
|
46
|
-
/**
|
|
47
|
-
* INVITEE is already a registered human.
|
|
48
|
-
* generateInvite throws INVITATION_INVITEE_ALREADY_REGISTERED.
|
|
49
|
-
*/
|
|
50
|
-
export const alreadyRegisteredInvitee = new ScenarioBuilder()
|
|
51
|
-
.avatar(INVITER, true)
|
|
52
|
-
.avatar(INVITEE, true)
|
|
53
|
-
.enableModule(INVITER, true)
|
|
54
|
-
.setIsTrusted(INV_MODULE, INVITER, true)
|
|
55
|
-
.trust(INV_MODULE, INVITER)
|
|
56
|
-
.balance(INVITER, INVITER, INVITATION_FEE)
|
|
57
|
-
.build();
|
|
58
|
-
/**
|
|
59
|
-
* INVITER holds own token (2 invites) and PROXY_B's token (1 invite).
|
|
60
|
-
* getRealInviters returns [{INVITER, 2}, {PROXY_B, 1}] — inviter first.
|
|
61
|
-
*/
|
|
62
|
-
export const multipleProxyInviters = new ScenarioBuilder()
|
|
63
|
-
.avatar(INVITER, true)
|
|
64
|
-
.avatar(PROXY_B, true)
|
|
65
|
-
.avatar(INVITEE, false)
|
|
66
|
-
.enableModule(INVITER, true)
|
|
67
|
-
.setIsTrusted(INV_MODULE, INVITER, true)
|
|
68
|
-
.trust(INV_MODULE, INVITER)
|
|
69
|
-
.trust(PROXY_B, INVITER)
|
|
70
|
-
.trust(INV_MODULE, PROXY_B)
|
|
71
|
-
.balance(INVITER, INVITER, INVITATION_FEE * 2n)
|
|
72
|
-
.balance(INVITER, PROXY_B, INVITATION_FEE)
|
|
73
|
-
.build();
|
|
74
|
-
/**
|
|
75
|
-
* INVITER holds only half of INVITATION_FEE for PROXY_A's token.
|
|
76
|
-
* findInvitePath throws INVITATION_INSUFFICIENT_BALANCE.
|
|
77
|
-
*/
|
|
78
|
-
export const insufficientBalance = new ScenarioBuilder()
|
|
79
|
-
.avatar(INVITER, true)
|
|
80
|
-
.avatar(PROXY_A, true)
|
|
81
|
-
.avatar(INVITEE, false)
|
|
82
|
-
.enableModule(INVITER, true)
|
|
83
|
-
.setIsTrusted(INV_MODULE, INVITER, true)
|
|
84
|
-
.trust(PROXY_A, INVITER)
|
|
85
|
-
.trust(INV_MODULE, PROXY_A)
|
|
86
|
-
.balance(INVITER, PROXY_A, INVITATION_FEE / 2n)
|
|
87
|
-
.build();
|
|
88
|
-
/**
|
|
89
|
-
* GNOSIS_GROUP_ADDRESS trusts PROXY_A (set1), which excludes PROXY_A as a real inviter.
|
|
90
|
-
* getRealInviters returns [].
|
|
91
|
-
*/
|
|
92
|
-
export const gnosisGroupExcluded = new ScenarioBuilder()
|
|
93
|
-
.avatar(INVITER, true)
|
|
94
|
-
.avatar(PROXY_A, true)
|
|
95
|
-
.avatar(INVITEE, false)
|
|
96
|
-
.enableModule(INVITER, true)
|
|
97
|
-
.setIsTrusted(INV_MODULE, INVITER, true)
|
|
98
|
-
.trust(PROXY_A, INVITER)
|
|
99
|
-
.trust(INV_MODULE, PROXY_A)
|
|
100
|
-
.trust(GNOSIS_GROUP_ADDRESS, PROXY_A)
|
|
101
|
-
.balance(INVITER, PROXY_A, INVITATION_FEE)
|
|
102
|
-
.build();
|
|
103
|
-
export const descriptions = {
|
|
104
|
-
personalCrcInvite: 'Inviter has enough personal CRC — standard happy path',
|
|
105
|
-
proxyInviterCrcInvite: 'Inviter holds CRC of a mutual-trust peer',
|
|
106
|
-
moduleNotEnabledInvite: 'Module not yet enabled — setup transactions required',
|
|
107
|
-
alreadyRegisteredInvitee: 'Invitee is already a registered human — invite must fail',
|
|
108
|
-
multipleProxyInviters: 'Multiple proxy inviters available — inviter\'s own token prioritized',
|
|
109
|
-
insufficientBalance: 'Inviter has a proxy candidate but not enough balance',
|
|
110
|
-
gnosisGroupExcluded: 'Proxy candidate is excluded because Gnosis group trusts them (set1)',
|
|
111
|
-
};
|