import type { Address, TransactionRequest, CirclesConfig, PathfindingResult } from '@aboutcircles/sdk-types'; import type { ReferralPreviewList, MigrationTxsBuilder, InviteMigrationOptions } from './types.js'; export interface ProxyInviter { address: Address; possibleInvites: number; } /** * Invitations handles invitation operations for Circles * Supports both referral invitations (new users) and direct invitations (existing Safe wallets) */ export declare class Invitations { private config; private rpcClient; private pathfinder; private trust; private token; private hubV2; private referralsModule; private invitationFarm; private invitationModuleContract; private gnosisPayGrantee?; constructor(config: CirclesConfig); /** * Check if the inviter has the invitation module enabled on their Safe * and is trusted by the invitation module. Returns setup transactions if needed. * * @param inviter - Address of the inviter (Safe wallet) * @returns Array of setup transactions (enableModule + trustInviter) needed before inviting */ ensureInviterSetup(inviter: Address): Promise; /** * Check whether the inviter is an eligible Gnosis Pay user with unclaimed * free invites. Returns 0 when the grantee contract is not configured or the * inviter is not eligible. * * @param inviter - Address of the inviter * @returns Number of free invites the inviter can still claim */ getClaimableFreeInvites(inviter: Address): Promise; /** * Build the free-invite transaction batch for a single invitation. * * Claiming a free invite grants the inviter +1 quota in the InvitationFarm, * which the subsequent `claimInvite()` consumes — all in one atomic batch, so * the inviter needs neither proxy inviters nor 96 CRC to fund the invite. * * @param inviter - Address of the inviter (lowercased) * @param transferData - Encoded data for the invitation transfer (invitee * address for direct invites, or ReferralsModule + createAccount calldata * for referrals) * @returns `[claimFreeInvite, claimInvite, safeTransferFrom]` */ private buildFreeInviteTransactions; /** * Save referral data to the referrals service * * @param inviter - Address of the inviter * @param privateKey - Private key generated for the new user * * @description * Sends a POST request to the referrals service to store referral data. */ saveReferralData(inviter: Address, privateKey: `0x${string}`): Promise; /** * List referrals for a given inviter with key previews * * @param inviter - Address of the inviter * @param limit - Maximum number of referrals to return (default 10) * @param offset - Number of referrals to skip for pagination (default 0) * @returns Paginated list of referral previews with masked keys */ listReferrals(inviter: Address, limit?: number, offset?: number): Promise; /** * Order real inviters by preference (best to worst) * * @param realInviters - Array of valid real inviters with their addresses and possible invites * @param inviter - Address of the current inviter (prioritized first) * @returns Ordered array of real inviters (best candidates first) * * @description * This function determines the optimal order for selecting real inviters. * Prioritizes the inviter's own tokens first, then others. */ private orderRealInviters; /** * Generate invitation transaction for a user who already has a Safe wallet but is not yet registered in Circles Hub * * @param inviter - Address of the inviter * @param invitee - Address of the invitee (must have an existing Safe wallet but NOT be registered in Circles Hub) * @returns Array of transactions to execute in order * * @description * This function: * 1. Verifies the invitee is NOT already registered as a human in Circles Hub * 2. Finds a path from inviter to invitation module using available proxy inviters * 3. Generates invitation data for the existing Safe wallet address * 4. Builds transaction batch with proper wrapped token handling * 5. Returns transactions ready to execute * * Note: The invitee MUST have a Safe wallet but MUST NOT be registered in Circles Hub yet. * If they are already registered, an error will be thrown. */ generateInvite(inviter: Address, invitee: Address): Promise; /** * Like {@link generateInvite}, but additionally builds the inviter's legacy * CRC migration into the same batch, token-disjoint by construction: the * migration pathfinder excludes every source token of the invitation path, * so the two flow matrices can run atomically without double-spending any * balance. The invitation legs come first (the invite has a hard 96 CRC * requirement; the migration takes whatever is left). A migration failure * never blocks the invite — it is skipped with `migrationAmount: 0n`. * * @param inviter - Address of the inviter * @param invitee - Address of the invitee (existing Safe, not yet registered) * @param migrationBuilder - The migration tx builder (a configured * `PermissionlessGroup` from `@aboutcircles/sdk-permissionless-groups`) * @param options - Optional migration knobs (`maxEdges`, extra exclusions) * @returns The combined invite + migration batch and the migrated amount */ generateInviteWithMigration(inviter: Address, invitee: Address, migrationBuilder: MigrationTxsBuilder, options?: InviteMigrationOptions): Promise<{ transactions: TransactionRequest[]; migrationAmount: bigint; }>; /** * Build the invite transfer transactions for an already-encoded * `transferData`, via the standard proxy-inviter path when available, else * the farm / Gnosis Pay fallback. Also reports `usedTokens` — the source * tokens of the underlying pathfinder path (empty for the free-invite path, * which moves no inviter CRC) — so callers can keep any additional flow * matrix in the same batch disjoint from the invite. */ private buildInviteTransactions; /** * Build the inviter's legacy-CRC migration excluding the invitation path's * source tokens, so both legs can run in one atomic batch without * double-spending any balance. The migration is opportunistic: any failure * is logged and swallowed so it never blocks the invitation itself. */ private buildDisjointMigration; /** * Build the funding + claim + transfer txs when the inviter has no proxy * inviters. Prefers the farm fallback (pay 96 CRC for quota); only when that * isn't affordable (insufficient balance / no path) does it fall back to a * Gnosis Pay free invite, if the inviter is eligible. Rethrows the original * farm error when no fallback applies. * * @param inviter - Address of the inviter (lowercased) * @param transferData - Encoded invitation transfer data * @returns The funding/claim/transfer transactions plus the source tokens of * the quota path (empty for the free-invite path, which moves no inviter CRC) */ private buildFarmOrFreeInviteTransactions; /** * Find a path from inviter to the invitation module for a specific proxy inviter * * @param inviter - Address of the inviter * @param proxyInviterAddress - Optional specific proxy inviter address to use for the path * @returns PathfindingResult containing the transfer path * * @description * This function finds a path from the inviter to the invitation module. * If proxyInviterAddress is provided, it will find a path using that specific token. * Otherwise, it will use the first available proxy inviter. */ findInvitePath(inviter: Address, proxyInviterAddress?: Address): Promise; /** * Find a fallback path from inviter to the farm destination * * @param inviter - Address of the inviter * @returns PathfindingResult containing the transfer path to the farm * * @description * This function finds a path from the inviter to the farm destination (0x9Eb51E6A39B3F17bB1883B80748b56170039ff1d) * using FARM_TO_TOKEN as the target token. Used when no standard proxy inviters are available. */ findFarmInvitePath(inviter: Address): Promise; /** * Get real inviters who have enough balance to cover invitation fees * * @param inviter - Address of the inviter * @returns Array of real inviters with their addresses and possible number of invitations * * @description * This function: * @description * set1 = addresses trusted by the Gnosis group (excluded from proxy inviters) * set2 = addresses that trust the inviter (potential token sources) * set3 = addresses trusted by the invitation module (can receive those tokens) * Proxy inviters = (set2 ∩ set3) − set1 * * Only (set2 ∩ set3) − set1 addresses are passed to the pathfinder as toTokens. * Wrapped ERC20 token addresses returned by the pathfinder are resolved back to their real * human avatar owners via getTokenInfoBatch before amounts are summed. */ getRealInviters(inviter: Address): Promise; /** * Generate a referral for inviting a new user * * @param inviter - Address of the inviter * @returns Object containing transactions, the generated private key, and whether farm was used * * @description * This function: * 1. Generates a new private key and signer address for the invitee * 2. Tries to find proxy inviters (accounts that trust inviter, trusted by module, NOT trusted by gnosis group) * 3. If no proxy inviters found, falls back to farm-based invitation: * - Sends 96 CRC to FARM_DESTINATION to increase quota on the invitation farm * - Then uses the farm (claimInvite + safeTransferFrom) to create the referral * 4. Builds transaction batch including transfers and invitation * 5. Uses generateInviteData to properly encode the Safe account creation data * 6. Saves the referral data (private key, signer, inviter) to database * 7. Returns transactions and the generated private key */ generateReferral(inviter: Address): Promise<{ transactions: TransactionRequest[]; privateKey: `0x${string}`; }>; /** * Like {@link generateReferral}, but additionally builds the inviter's * legacy CRC migration into the same batch, token-disjoint by construction: * the migration pathfinder excludes every source token of the referral * path, so the two flow matrices can run atomically without double-spending * any balance. The referral legs come first (the invite has a hard 96 CRC * requirement; the migration takes whatever is left). A migration failure * never blocks the referral — it is skipped with `migrationAmount: 0n`. * * This replaces the broken pattern of concatenating independently built * `migration().txs` and `generateReferral().transactions`: both pathfinder * runs see the same chain state, so they source the same tokens and the * second unwrap/flow-matrix reverts on-chain (`ERC20InsufficientBalance` / * `ERC1155InsufficientBalance`). * * @param inviter - Address of the inviter * @param migrationBuilder - The migration tx builder (a configured * `PermissionlessGroup` from `@aboutcircles/sdk-permissionless-groups`) * @param options - Optional migration knobs (`maxEdges`, extra exclusions) * @returns The combined referral + migration batch, the generated private * key, and the migrated amount */ generateReferralWithMigration(inviter: Address, migrationBuilder: MigrationTxsBuilder, options?: InviteMigrationOptions): Promise<{ transactions: TransactionRequest[]; privateKey: `0x${string}`; migrationAmount: bigint; }>; /** * Generate invitation data based on whether addresses need Safe account creation or already have Safe wallets * * @param addresses - Array of addresses to check and encode * @param useSafeCreation - If true, uses ReferralsModule to create Safe accounts (for new users without wallets) * @returns Encoded data for the invitation transfer * * @description * Two modes: * 1. Direct invitation (useSafeCreation = false): Encodes addresses directly for existing Safe wallets * 2. Safe creation (useSafeCreation = true): Uses ReferralsModule to create Safe accounts for new users * * Note: Addresses passed here should NEVER be registered humans in the hub (that's validated before calling this) */ generateInviteData(addresses: Address[], useSafeCreation?: boolean): Promise<`0x${string}`>; /** * Predicts the pre-made Safe address for a given signer without deploying it * Uses CREATE2 with ACCOUNT_INITIALIZER_HASH and ACCOUNT_CREATION_CODE_HASH via SAFE_PROXY_FACTORY * * @param signer - The offchain public address chosen by the origin inviter as the pre-deployment key * @returns The deterministic Safe address that would be deployed for the signer * * @description * This implements the same logic as the ReferralsModule.computeAddress() contract function: * ```solidity * bytes32 salt = keccak256(abi.encodePacked(ACCOUNT_INITIALIZER_HASH, uint256(uint160(signer)))); * predictedAddress = address( * uint160( * uint256( * keccak256( * abi.encodePacked(bytes1(0xff), address(SAFE_PROXY_FACTORY), salt, ACCOUNT_CREATION_CODE_HASH) * ) * ) * ) * ); * ``` */ computeAddress(signer: Address): Address; /** * Generate secrets and derive signer addresses for multiple invitations * @param count Number of secrets to generate */ generateSecrets(count: number): Array<{ secret: `0x${string}`; signer: Address; }>; } //# sourceMappingURL=Invitations.d.ts.map