@aboutcircles/sdk 0.1.9 → 0.1.11

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.
@@ -1,10 +1,8 @@
1
1
  import { ValidationError } from '@aboutcircles/sdk-utils';
2
2
  import { SdkError } from '../errors';
3
3
  import { BaseGroupContract } from '@aboutcircles/sdk-core';
4
- import { encodeAbiParameters, parseAbiParameters, encodeFunctionData } from 'viem';
5
- import { referralsModuleAbi } from '@aboutcircles/sdk-abis';
6
- import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
7
4
  import { CommonAvatar } from './CommonAvatar';
5
+ import { Invitations, InviteFarm } from '@aboutcircles/sdk-invitations';
8
6
  /**
9
7
  * HumanAvatar class implementation
10
8
  * Provides a simplified, user-friendly wrapper around Circles protocol for human avatars
@@ -96,250 +94,133 @@ export class HumanAvatar extends CommonAvatar {
96
94
  // ============================================================================
97
95
  // Trust methods are inherited from CommonAvatar
98
96
  // ============================================================================
99
- // Invitation methods
100
- invite = {
97
+ // Invitation methods using the new Invitations module
98
+ invitation = {
101
99
  /**
102
- * Invite someone to Circles by escrowing 100 CRC tokens
100
+ * Get a referral code for inviting a new user who doesn't have a Safe wallet yet
103
101
  *
104
- * This batches two transactions atomically:
105
- * 1. Establishes trust with the invitee (with indefinite expiry)
106
- * 2. Transfers 100 of your personal CRC tokens to the InvitationEscrow contract
102
+ * This function:
103
+ * 1. Generates a new private key and signer address for the invitee
104
+ * 2. Finds proxy inviters (intermediaries in trust graph)
105
+ * 3. Builds transaction batch to transfer 96 CRC to the invitation module
106
+ * 4. Saves the referral data to the backend
107
+ * 5. Returns transactions and the generated private key
107
108
  *
108
- * The tokens are held in escrow until the invitee redeems the invitation by registering.
109
+ * The private key should be shared with the invitee to claim their account.
109
110
  *
110
111
  * Requirements:
111
- * - You must have at least 100 CRC available
112
- * - Invitee must not be already registered in Circles
113
- * - You can only have one active invitation per invitee
112
+ * - You must have at least 96 CRC available (directly or via proxy inviters)
113
+ * - Referrals service must be configured
114
114
  *
115
- * @param invitee The address to invite
116
- * @returns Transaction response
115
+ * @returns Object containing transactions to execute and the private key to share
117
116
  *
118
117
  * @example
119
118
  * ```typescript
120
- * // Invite someone with 100 CRC (automatically establishes trust)
121
- * await avatar.invite.send('0x123...');
119
+ * const { transactions, privateKey } = await avatar.invitation.getReferralCode();
120
+ * // Execute transactions
121
+ * await avatar.runner.sendTransaction(transactions);
122
+ * // Share privateKey with invitee
122
123
  * ```
123
124
  */
124
- send: async (invitee) => {
125
- //@todo add replenish/unwrap logic
126
- // Create trust transaction (indefinite trust)
127
- const trustTx = this.core.hubV2.trust(invitee, BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFF'));
128
- // Get the token ID for this avatar's personal token
129
- const tokenId = await this.core.hubV2.toTokenId(this.address);
130
- // ABI-encode the invitee address as 32 bytes
131
- const encodedInvitee = encodeAbiParameters(parseAbiParameters('address'), [invitee]);
132
- // Create the safeTransferFrom transaction to the InvitationEscrow contract
133
- const transferTx = this.core.hubV2.safeTransferFrom(this.address, this.core.config.invitationEscrowAddress, tokenId, BigInt(100e18), encodedInvitee);
134
- // Batch both transactions: trust + invitation transfer
135
- return await this.runner.sendTransaction([trustTx, transferTx]);
125
+ getReferralCode: async () => {
126
+ const invitations = new Invitations(this.core.config);
127
+ return await invitations.generateReferral(this.address);
136
128
  },
137
129
  /**
138
- * Revoke a previously sent invitation
130
+ * Invite a user who already has a Safe wallet but is not yet registered in Circles
139
131
  *
140
- * This returns the escrowed tokens (with demurrage applied) back to you
141
- * as wrapped ERC20 tokens.
132
+ * Use this when inviting someone who has an existing Safe wallet but is not
133
+ * yet registered in Circles Hub.
142
134
  *
143
- * @param invitee The address whose invitation to revoke
144
- * @returns Transaction response
135
+ * @param invitee Address of the invitee (must have existing Safe wallet, NOT registered in Circles)
136
+ * @returns Array of transactions to execute
145
137
  *
146
138
  * @example
147
139
  * ```typescript
148
- * await avatar.invite.revoke('0x123...');
140
+ * const transactions = await avatar.invitation.invite('0xInviteeAddress');
141
+ * await avatar.runner.sendTransaction(transactions);
149
142
  * ```
150
143
  */
151
- revoke: async (invitee) => {
152
- const revokeTx = this.core.invitationEscrow.revokeInvitation(invitee);
153
- return await this.runner.sendTransaction([revokeTx]);
144
+ invite: async (invitee) => {
145
+ const invitations = new Invitations(this.core.config);
146
+ return await invitations.generateInvite(this.address, invitee);
154
147
  },
155
148
  /**
156
- * Revoke all active invitations at once
149
+ * Get proxy inviters who can facilitate invitations
157
150
  *
158
- * This returns all escrowed tokens (with demurrage applied) back to you
159
- * as wrapped ERC20 tokens in a single transaction.
151
+ * Proxy inviters are addresses that:
152
+ * - Trust this avatar (or have mutual trust)
153
+ * - Are trusted by the invitation module
154
+ * - Have sufficient balance to cover invitation fees (96 CRC per invite)
160
155
  *
161
- * @returns Transaction response
156
+ * @returns Array of proxy inviters with their addresses and possible invite counts
162
157
  *
163
158
  * @example
164
159
  * ```typescript
165
- * await avatar.invite.revokeAll();
166
- * ```
167
- */
168
- revokeAll: async () => {
169
- const revokeAllTx = this.core.invitationEscrow.revokeAllInvitations();
170
- return await this.runner.sendTransaction([revokeAllTx]);
171
- },
172
- /**
173
- * Redeem an invitation received from an inviter
174
- *
175
- * This claims the escrowed tokens from a specific inviter and refunds
176
- * all other inviters' escrows back to them.
177
- *
178
- * @param inviter The address of the inviter whose invitation to redeem
179
- * @returns Transaction response
180
- *
181
- * @example
182
- * ```typescript
183
- * // Get all inviters first
184
- * const inviters = await avatar.invite.getInviters();
185
- *
186
- * // Redeem invitation from the first inviter
187
- * await avatar.invite.redeem(inviters[0]);
188
- * ```
189
- */
190
- // @todo check if it functionable
191
- redeem: async (inviter) => {
192
- const redeemTx = this.core.invitationEscrow.redeemInvitation(inviter);
193
- return await this.runner.sendTransaction([redeemTx]);
194
- },
195
- /**
196
- * Get all addresses that have sent invitations to you
197
- *
198
- * @returns Array of inviter addresses
199
- *
200
- * @example
201
- * ```typescript
202
- * const inviters = await avatar.invite.getInviters();
203
- * console.log(`You have ${inviters.length} pending invitations`);
160
+ * const proxyInviters = await avatar.invitation.getProxyInviters();
161
+ * proxyInviters.forEach(inviter => {
162
+ * console.log(`${inviter.address}: can invite ${inviter.possibleInvites} people`);
163
+ * });
204
164
  * ```
205
165
  */
206
- getInviters: async () => {
207
- return await this.core.invitationEscrow.getInviters(this.address);
166
+ getProxyInviters: async () => {
167
+ const invitations = new Invitations(this.core.config);
168
+ return await invitations.getRealInviters(this.address);
208
169
  },
209
170
  /**
210
- * Get all addresses you have invited
171
+ * Find a path from this avatar to the invitation module
211
172
  *
212
- * @returns Array of invitee addresses
173
+ * @param proxyInviterAddress Optional specific proxy inviter to route through
174
+ * @returns PathfindingResult containing the transfer path
213
175
  *
214
176
  * @example
215
177
  * ```typescript
216
- * const invitees = await avatar.invite.getInvitees();
217
- * console.log(`You have invited ${invitees.length} people`);
178
+ * const path = await avatar.invitation.findInvitePath();
179
+ * console.log('Max flow:', path.maxFlow);
218
180
  * ```
219
181
  */
220
- getInvitees: async () => {
221
- return await this.core.invitationEscrow.getInvitees(this.address);
182
+ findInvitePath: async (proxyInviterAddress) => {
183
+ const invitations = new Invitations(this.core.config);
184
+ return await invitations.findInvitePath(this.address, proxyInviterAddress);
222
185
  },
223
186
  /**
224
- * Get the escrowed amount and days since escrow for a specific invitation
187
+ * Compute the deterministic Safe address for a given signer
225
188
  *
226
- * The amount returned has demurrage applied, so it decreases over time.
189
+ * Uses CREATE2 to predict the Safe address without deployment.
227
190
  *
228
- * @param inviter The inviter address (when checking invitations you received)
229
- * @param invitee The invitee address (when checking invitations you sent)
230
- * @returns Object with escrowedAmount (in atto-circles) and days since escrow
191
+ * @param signer The signer public address
192
+ * @returns The deterministic Safe address
231
193
  *
232
194
  * @example
233
195
  * ```typescript
234
- * // Check an invitation you sent
235
- * const { escrowedAmount, days_ } = await avatar.invite.getEscrowedAmount(
236
- * avatar.address,
237
- * '0xinvitee...'
238
- * );
239
- * console.log(`Escrowed: ${CirclesConverter.attoCirclesToCircles(escrowedAmount)} CRC`);
240
- * console.log(`Days since escrow: ${days_}`);
241
- *
242
- * // Check an invitation you received
243
- * const { escrowedAmount, days_ } = await avatar.invite.getEscrowedAmount(
244
- * '0xinviter...',
245
- * avatar.address
246
- * );
196
+ * const safeAddress = avatar.invitation.computeAddress('0xSignerAddress');
247
197
  * ```
248
198
  */
249
- getEscrowedAmount: async (inviter, invitee) => {
250
- return await this.core.invitationEscrow.getEscrowedAmountAndDays(inviter, invitee);
199
+ computeAddress: (signer) => {
200
+ const invitations = new Invitations(this.core.config);
201
+ return invitations.computeAddress(signer);
251
202
  },
252
203
  /**
253
- * Generate new invitations and return associated secrets and signer addresses
254
- *
255
- * This function:
256
- * 1. Calls invitationFarm.claimInvites() to get invitation IDs via eth_call
257
- * 2. Generates random secrets for each invitation
258
- * 3. Derives signer addresses from the secrets using ECDSA
259
- * 4. Batches the claimInvites write call with safeBatchTransferFrom to transfer
260
- * invitation tokens (96 CRC each) to the invitation module
261
- * 5. Returns the list of secrets and corresponding signers
262
- *
263
- * The data field in the batch transfer contains the count of generated secrets,
264
- * which the contract uses to validate the transfer.
204
+ * Generate new invitations using the InvitationFarm
265
205
  *
266
- * @param numberOfInvites The number of invitations to generate
267
- * @returns Promise containing arrays of secrets and signers for each generated invitation
268
- *
269
- * @throws {SdkError} If the transaction fails or invitations cannot be claimed
206
+ * @param count Number of invitations to generate
207
+ * @returns Promise containing secrets, signers, and transaction receipt
270
208
  *
271
209
  * @example
272
210
  * ```typescript
273
- * // Generate 5 invitations
274
- * const result = await avatar.invite.generateInvites(5n);
275
- *
276
- * console.log('Generated invitations:');
277
- * result.secrets.forEach((secret, index) => {
278
- * console.log(`Invitation ${index + 1}:`);
279
- * console.log(` Secret: ${secret}`);
280
- * console.log(` Signer: ${result.signers[index]}`);
211
+ * const result = await avatar.invitation.generateInvites(5);
212
+ * result.secrets.forEach((secret, i) => {
213
+ * console.log(`Invite ${i + 1}: ${result.signers[i]}`);
281
214
  * });
282
215
  * ```
283
216
  */
284
- generateInvites: async (numberOfInvites) => {
285
- if (numberOfInvites <= 0n) {
286
- throw SdkError.operationFailed('generateInvites', 'numberOfInvites must be greater than 0');
287
- }
288
- // Step 1: Call eth_call to claimInvites to get invitation IDs (read-only simulation)
289
- // This simulates the claimInvites call without actually modifying state
290
- // to get the IDs that would be returned
291
- const ids = (await this.core.invitationFarm.read('claimInvites', [numberOfInvites], {
292
- from: this.address
293
- }));
294
- console.log("ids", ids);
295
- if (!ids || ids.length === 0) {
296
- throw SdkError.operationFailed('generateInvites', 'No invitation IDs returned from claimInvites');
297
- }
298
- // Step 2: Generate random secrets and derive signers
299
- const secrets = [];
300
- const signers = [];
301
- for (let i = 0; i < numberOfInvites; i++) {
302
- // Generate a random private key
303
- const privateKey = generatePrivateKey();
304
- secrets.push(privateKey);
305
- // Derive the signer address from the private key
306
- const account = privateKeyToAccount(privateKey);
307
- signers.push(account.address.toLowerCase());
308
- }
309
- // Step 3: Get invitation module address
310
- const invitationModuleAddress = await this.core.invitationFarm.invitationModule();
311
- // Step 4: Referrals module address
312
- const referralsModuleAddress = this.core.config.referralsModuleAddress;
313
- // Step 5: Build the batch transaction
314
- // - claimInvites write call (to actually claim the invites)
315
- // - safeBatchTransferFrom to transfer invitation tokens to the invitation module
316
- // Create the claimInvites write transaction
317
- const claimInvitesWriteTx = this.core.invitationFarm.claimInvites(numberOfInvites);
318
- // Step 6: Encode the createAccounts function call to the referrals module
319
- // This call will be executed by the invitation module via the generic call proxy
320
- const createAccountsCallData = encodeFunctionData({
321
- abi: referralsModuleAbi,
322
- functionName: 'createAccounts',
323
- args: [signers],
324
- });
325
- // Step 7: Create safeBatchTransferFrom transaction to transfer invitation tokens to the invitation module
326
- // - from: this avatar
327
- // - to: invitation module
328
- // - ids: the invitation IDs returned from claimInvites
329
- // - amounts: all 96 CRC (96 * 10^18) per invitation
330
- // - data: encoded as (address referralsModule, bytes callData) for the invitation module to execute
331
- const amounts = [];
332
- for (let i = 0; i < ids.length; i++) {
333
- amounts.push(BigInt(96e18)); // 96 CRC in atto-circles
334
- }
335
- // Encode the data as (address, bytes) - referrals module address + createAccounts call data
336
- const encodedData = encodeAbiParameters(parseAbiParameters('address, bytes'), [referralsModuleAddress, createAccountsCallData]);
337
- const batchTransferTx = this.core.hubV2.safeBatchTransferFrom(this.address, invitationModuleAddress, ids, amounts, encodedData);
338
- // Step 7: Execute the batch transaction
339
- const receipt = await this.runner.sendTransaction([claimInvitesWriteTx, batchTransferTx]);
217
+ generateInvites: async (count) => {
218
+ const farm = new InviteFarm(this.core.config);
219
+ const result = await farm.generateInvites(this.address, count);
220
+ const receipt = await this.runner.sendTransaction(result.transactions);
340
221
  return {
341
- secrets,
342
- signers,
222
+ secrets: result.invites.map((inv) => inv.secret),
223
+ signers: result.invites.map((inv) => inv.signer),
343
224
  transactionReceipt: receipt,
344
225
  };
345
226
  },