Deposit Portals
Receive ERC-20 transfers at a Portal, then credit private notes or recover uncredited tokens.
Choose a Portal#
A Portal receives ordinary ERC-20 transfers from wallets or exchanges that cannot call the Pool directly. Funds arrive at the Portal first. A later Pool call creates the private note, so the sender's transfer receipt alone does not mean the recipient has a spendable private balance.
FuyuDepositRecoveryPortal links to a public owner and their directory registration. FuyuAnonymousPortal uses an invoice tree, encrypted profile and recovery commitment without publishing a receiving-owner field. Both contracts expose funding assets, amounts, credit transactions and timing. The anonymous Portal omits the owner association; its funding transfer is still public.
Owner-linked Portal#
Construction fixes owner, Pool, directory, Pool domain and the initial receiving-descriptor hash. The directory must point to the same Pool/domain, and the owner's starting descriptor must be active. Only that owner can credit private notes or recover uncredited tokens.
Credit checks the current directory version, controller/owner/key descriptor and recovery material. Read the registration again when preparing a credit. A rotated descriptor can make an old request stale, and a keeper cannot resolve that by choosing another receiver.
An ERC-20 balance does not identify who funded it. Transfer logs can help you match public transfers, but the Portal has no per-payer claim ledger. Recovery sends tokens to the fixed owner. Explain that to users before offering an owner-linked Portal for shared payment collection.
| Field or event | Meaning |
|---|---|
| owner | The public account that can credit and recover tokens |
| pool / poolDomain | The Pool that receives private credit |
| directory | The owner's receiving registrations |
| initialDescriptorHash | The descriptor selected at deployment |
| Credited event | The asset and amount credited with a directory version |
| Recovered event | Uncredited tokens returned to the owner |
| StaleDescriptor / InvalidBinding | Reload the directory or check the deployed contracts |
| InvalidAsset / InvalidAmount / TokenCallFailed | Check the asset, amount and token balance changes |
Anonymous invoices#
The anonymous factory deploys a Portal from signed Setup terms. Setup contains invoiceDomain, invoiceRoot, invoiceCount, encryptedProfileHash, recoveryCommitment, setupSigner, passkeyDiscoveryKey and deadline. The factory connects it to a Pool and records authenticated discovery mappings. Save the setup payload and factory address with your recovery data.
The depth-eight invoice tree covers the declared invoice range. Each Invoice has an asset, minimum and maximum amount, recovery salt and owner commitment. sweep takes the entire asset balance only if it is within those bounds. Set both bounds equal for an exact-amount invoice. invoiceLeaf(index, invoice) includes the Pool and invoice domain; sweep checks the next invoice with eight sibling hashes. Sending another asset or amount cannot change its terms.
struct Invoice {
address asset;
uint128 minAmount;
uint128 maxAmount;
bytes32 recoverySalt;
uint256 ownerCommitment;
}
function invoiceLeaf(uint16 _index, Invoice calldata _invoice)
public view returns (bytes32);
function sweep(
Invoice calldata _invoice, bytes32[8] calldata _siblings,
bytes calldata _initialEncryptedProfile
) external;
function recoverUncredited(
address _asset, uint256 _amount,
address _fallbackDestination, bytes32 _recoverySecret
) external;Credit a transfer#
Before showing a payment address, check the deployed Portal and publish or save its invoice witness. Compare the asset contract, atomic amount and invoice index with the payer's review. For a predicted address, verify the factory, Setup and any code already deployed there.
Anyone can call sweep once the entire asset balance is within the invoice's minimum/maximum range. The first credit needs the encrypted profile that matches the setup hash. Later credits use the Portal's existing Pool profile pointer and funding nonce. A successful sweep advances the invoice; a failed sweep leaves it unchanged and cannot redirect the note.
Read Swept for invoice progress, InvoiceWitnessPublished for the invoice data and the Pool's Deposited event for the commitment. Recover the recipient note and check that commitment. Until credit succeeds, funds at the Portal remain public tokens, even if the sender's wallet calls the transfer complete.
Recover uncredited tokens#
The recovery commitment includes the chain, Pool, invoice domain, fallback destination and secret. Opening it lets a caller recover only to that destination. Once recovery opens, private sweeps stop permanently. Choose recovery when you want to recover the remaining tokens rather than complete the invoice.
Recovery reaches only tokens still at the Portal. Notes already credited in the Pool stay there. Save the recovery secret and destination before sharing the address, and keep the secret out of analytics and logs. Using it onchain makes the opening public.
InvalidInvoice means the invoice or tree proof failed. InvalidProfile means the profile hash differs. InvalidRecovery means the recovery opening failed. Closed means recovery has already stopped private credit. Check the existing Portal state before creating another address or asking the payer to resend.
Show payment progress#
Show the chain, token address, atomic amount, Portal address and current state. Use separate states for Awaiting transfer, Awaiting credit, Credited and Recovery opened. When a sponsored keeper is unavailable, a supported direct-wallet flow can call the same sweep and pay its gas.
For recurring receiving, read discovery and invoice progress from the chain. Track transfers separately from credited invoices, especially when several transfers add up to one invoice. Balance arithmetic cannot authenticate a sender, and Portal recovery cannot retrieve funds already credited as private notes.