@aboutcircles/sdk 0.1.10 → 0.1.12

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,252 +94,81 @@ export class HumanAvatar extends CommonAvatar {
96
94
  // ============================================================================
97
95
  // Trust methods are inherited from CommonAvatar
98
96
  // ============================================================================
99
- // Invitation methods
100
- invite = {
97
+ // ============================================================================
98
+ // Invitation methods using the Invitations module
99
+ // ============================================================================
100
+ _invitations = new Invitations(this.core.config);
101
+ _inviteFarm = new InviteFarm(this.core.config);
102
+ invitation = {
101
103
  /**
102
- * Invite someone to Circles by escrowing 100 CRC tokens
103
- *
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
107
- *
108
- * The tokens are held in escrow until the invitee redeems the invitation by registering.
109
- *
110
- * 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
114
- *
115
- * @param invitee The address to invite
116
- * @returns Transaction response
117
- *
118
- * @example
119
- * ```typescript
120
- * // Invite someone with 100 CRC (automatically establishes trust)
121
- * await avatar.invite.send('0x123...');
122
- * ```
104
+ * Get a referral code for inviting a new user who doesn't have a Safe wallet yet.
105
+ * Generates private key, finds proxy inviters, builds tx batch, saves referral data.
106
+ * @returns Transactions to execute and the private key to share with invitee
123
107
  */
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]);
108
+ getReferralCode: async () => {
109
+ return this._invitations.generateReferral(this.address);
136
110
  },
137
111
  /**
138
- * Revoke a previously sent invitation
139
- *
140
- * This returns the escrowed tokens (with demurrage applied) back to you
141
- * as wrapped ERC20 tokens.
142
- *
143
- * @param invitee The address whose invitation to revoke
144
- * @returns Transaction response
145
- *
146
- * @example
147
- * ```typescript
148
- * await avatar.invite.revoke('0x123...');
149
- * ```
112
+ * Invite a user who already has a Safe wallet but is not yet registered in Circles.
113
+ * @param invitee Address of the invitee (must have existing Safe, NOT registered in Circles)
150
114
  */
151
- revoke: async (invitee) => {
152
- const revokeTx = this.core.invitationEscrow.revokeInvitation(invitee);
153
- return await this.runner.sendTransaction([revokeTx]);
115
+ invite: async (invitee) => {
116
+ return this._invitations.generateInvite(this.address, invitee);
154
117
  },
155
118
  /**
156
- * Revoke all active invitations at once
157
- *
158
- * This returns all escrowed tokens (with demurrage applied) back to you
159
- * as wrapped ERC20 tokens in a single transaction.
160
- *
161
- * @returns Transaction response
162
- *
163
- * @example
164
- * ```typescript
165
- * await avatar.invite.revokeAll();
166
- * ```
119
+ * Get proxy inviters who can facilitate invitations.
120
+ * These are addresses that trust this avatar, are trusted by the invitation module,
121
+ * and have sufficient balance (96 CRC per invite).
167
122
  */
168
- revokeAll: async () => {
169
- const revokeAllTx = this.core.invitationEscrow.revokeAllInvitations();
170
- return await this.runner.sendTransaction([revokeAllTx]);
123
+ getProxyInviters: async () => {
124
+ return this._invitations.getRealInviters(this.address);
171
125
  },
172
126
  /**
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
- * ```
127
+ * Find a path from this avatar to the invitation module.
128
+ * @param proxyInviterAddress Optional specific proxy inviter to route through
189
129
  */
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]);
130
+ findInvitePath: async (proxyInviterAddress) => {
131
+ return this._invitations.findInvitePath(this.address, proxyInviterAddress);
194
132
  },
195
133
  /**
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`);
204
- * ```
134
+ * Compute the deterministic Safe address for a given signer using CREATE2.
135
+ * @param signer The signer public address
205
136
  */
206
- getInviters: async () => {
207
- return await this.core.invitationEscrow.getInviters(this.address);
137
+ computeAddress: (signer) => {
138
+ return this._invitations.computeAddress(signer);
208
139
  },
209
140
  /**
210
- * Get all addresses you have invited
211
- *
212
- * @returns Array of invitee addresses
213
- *
214
- * @example
215
- * ```typescript
216
- * const invitees = await avatar.invite.getInvitees();
217
- * console.log(`You have invited ${invitees.length} people`);
218
- * ```
141
+ * Generate batch invitations using the InvitationFarm.
142
+ * @param count Number of invitations to generate
219
143
  */
220
- getInvitees: async () => {
221
- return await this.core.invitationEscrow.getInvitees(this.address);
144
+ generateInvites: async (count) => {
145
+ const result = await this._inviteFarm.generateInvites(this.address, count);
146
+ const receipt = await this.runner.sendTransaction(result.transactions);
147
+ return {
148
+ secrets: result.invites.map((inv) => inv.secret),
149
+ signers: result.invites.map((inv) => inv.signer),
150
+ transactionReceipt: receipt,
151
+ };
222
152
  },
223
- /**
224
- * Get the escrowed amount and days since escrow for a specific invitation
225
- *
226
- * The amount returned has demurrage applied, so it decreases over time.
227
- *
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
231
- *
232
- * @example
233
- * ```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
- * );
247
- * ```
248
- */
249
- getEscrowedAmount: async (inviter, invitee) => {
250
- return await this.core.invitationEscrow.getEscrowedAmountAndDays(inviter, invitee);
153
+ /** Get the remaining invite quota for this avatar */
154
+ getQuota: async () => {
155
+ return this._inviteFarm.getQuota(this.address);
156
+ },
157
+ /** Get the invitation fee (96 CRC) */
158
+ getInvitationFee: async () => {
159
+ return this._inviteFarm.getInvitationFee();
160
+ },
161
+ /** Get the invitation module address from the farm */
162
+ getInvitationModule: async () => {
163
+ return this._inviteFarm.getInvitationModule();
251
164
  },
252
165
  /**
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.
265
- *
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
270
- *
271
- * @example
272
- * ```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]}`);
281
- * });
282
- * ```
166
+ * List referrals for this avatar with key previews.
167
+ * @param limit Max referrals to return (default 10)
168
+ * @param offset Pagination offset (default 0)
283
169
  */
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]);
340
- return {
341
- secrets,
342
- signers,
343
- transactionReceipt: receipt,
344
- };
170
+ listReferrals: async (limit = 10, offset = 0) => {
171
+ return this._inviteFarm.listReferrals(this.address, limit, offset);
345
172
  },
346
173
  };
347
174
  // Personal token / Minting methods