Experimental API
Everything on this page is
@experimental. Each option, member and export below may change or be removed in a minor release. The rest of the API, every name api.md does not mark experimental, is kept after 0.6.0. Build on an experimental name only where you can follow its changes from release to release.
Each section says what the item does, its default, when to use it, and which account kind it applies to: a Fluent ID, an External wallet, or both. The exact types are in api.md.
| Name | Where it appears | Section |
|---|---|---|
privyAdapter, FluentPrivyAdapter | FluentWidgetProps | privyAdapter |
wallet, FluentExternalWalletState, FluentWalletChoice | FluentWidgetProps, FluentWidgetRenderContext | wallet |
execution, FluentWidgetExecution, FluentWidgetExecutionSource | FluentWidgetConfig | execution |
userOperationGas, FluentUserOperationGas, FluentFixedAccountGas, FluentFixedPaymasterGas, FluentUserOperationGasError, FluentUserOperationGasErrorCode | FluentBatchOperationExecuteOptions | userOperationGas |
paymaster, timing, FluentExecuteTiming, FluentExecuteTimingInstants | FluentExecuteResult | paymaster and timing |
gasPayment.ethValueByToken, FluentGasPaymentEthRates | FluentWidgetConfig | gasPayment.ethValueByToken |
customOAuthRedirectUrl | FluentWidgetConfig | customOAuthRedirectUrl |
swapper | FluentWidgetConfig | swapper |
sponsorshipUrl | FluentWidgetConfig | sponsorshipUrl |
privyClientId | FluentWidgetConfig | privyClientId |
privyAdapter
FluentWidgetProps.privyAdapter takes a FluentPrivyAdapter: the provider component and the
hooks of Privy's React SDK, taken from the host's own runtime
(api.md).
- What it does: the widget uses the adapter's provider and hooks in place of the web SDK it bundles, so Direct login, its restore on a later page load and the Signer's signatures run through the host's SDK.
- Default: none. Without it the widget uses its bundled web SDK, which is what a web App needs.
- When to use it: only in a host that runs Privy's native SDK instead of the web one.
- Applies to: a Fluent ID, and an External wallet's
linkX(). Connecting an External wallet does not use it. - Keep it stable: pass one adapter object for the life of the page. An adapter whose provider component differs from the last one remounts everything the widget renders.
wallet
FluentWidgetProps.wallet takes a FluentExternalWalletState: an External wallet the host
connects itself (api.md).
- What it does: the widget uses the host's state as its External wallet instead of
connecting one itself. The state says whether a wallet is
configuredandconnected, itsaddress,chainIdandwalletClient, and gives the widgetopen,disconnectandswitchChainto call.reconnectingistruewhile a wallet from an earlier visit is being restored; leaving it out makes the widget report"disconnected"a little earlier on a reload. - Inline choices:
choiceslistsFluentWalletChoiceentries (id,name, an optionalicon, andhandofffor a wallet that opens its own window or a QR code) that the connect dialog shows inline; the widget callsconnectChoice(id)with the one picked. Without them the host'sopen()picker is used (api.md). - Default: none. Omitted, or
null, the widget connects External wallets itself. - When to use it: when the App already manages its own wallet connection and the widget has to act for that same wallet.
- Applies to: an External wallet. An External wallet's Batch operation is executed through
walletClient, so the state needsconnectedset totrueand awalletClientfor one to be sent.
FluentWidgetRenderContext.wallet (also on useFluentWidget()) is the External wallet state the
widget is using: the host's when the prop is passed, the widget's own otherwise
(api.md).
execution
FluentWidgetConfig.execution takes a FluentWidgetExecution, every field optional
(api.md). Both sources are a FluentWidgetExecutionSource,
"chain" or "bundler" (api.md).
| Field | Default | What it sets |
|---|---|---|
feeSource | "chain" | Where a Fluent ID's operation takes its fee quote from: the Fluent chain, or the bundler. |
receiptSource | "chain" | Where the widget watches for the operation's inclusion: the Fluent chain, or the bundler. |
chainCheckBeforeSign | false | true reads the Signer's chain before every signature, one extra round trip each. |
- Default: the values above, on both Networks. A
"chain"source that fails falls back to the bundler on its own, within the operation that needed it. The defaults are the recommended setting. - When to use it: leave it out. Set one field to
"bundler"to take that one answer from the bundler for every operation, for example while diagnosing slow operations with Fluent. - Applies to: a Fluent ID.
chainCheckBeforeSigncovers both its Batch operations and itssignMessageandsignTypedDatasignatures. An External wallet ignoresexecution. - Invalid values:
<FluentWidget>throwsFluentWidgetConfigErrorfrom its render, withcodeinvalid_fee_source,invalid_receipt_sourceorinvalid_chain_check_before_sign(errors.md).
userOperationGas
userOperationGas is an option of execute(), on FluentBatchOperationExecuteOptions. It takes
a FluentUserOperationGas, one of three shapes (api.md):
| Shape | What the widget does |
|---|---|
{ callGasLimit } | Uses this execution gas limit for the whole Batch operation and estimates the rest; the gas of a Gas token approval, explicit or the one the widget adds automatically, is spent inside this limit. |
{ callGasBuffer: { percentage?, fixed? } } | Estimates, then adds percentage percent (rounded up), then fixed gas. |
{ skipEstimation: true, ... } | Estimates nothing: the App fixes the account's limits, and either fixes the paymaster's too or leaves them to the paymaster. |
With skipEstimation: true, the account's three limits are a FluentFixedAccountGas
(callGasLimit, verificationGasLimit, preVerificationGas, all required), and the
paymaster's two are a FluentFixedPaymasterGas (paymasterVerificationGasLimit and
paymasterPostOpGasLimit): pass both, or neither to use the limits the paymaster authorizes
(api.md, api.md).
For a Gas token payment, whatever the policy, the widget asks the paymaster for a
paymasterPostOpGasLimit of at least 50 000 gas: the paymaster charges the Gas token after
the operation, and ZeroDev refuses to authorize a USDnr payment whose request leaves that limit
at 0 or out. A larger limit, fixed or estimated, is asked for as it is. A skipEstimation policy
that fixes paymasterPostOpGasLimit below 50 000 for a Gas token payment therefore rejects with
paymaster_gas_mismatch before anything is signed.
- Default: omitted, the widget estimates every limit. Omitting it is the recommended setting.
- When to use it: a buffer when an operation's estimate runs short; fixed limits when the App knows them and wants no estimate.
- Applies to: the policy's shape is checked at the start of every
execute(), before the Review, whatever the account kind, so an invalid shape rejects for an External wallet too. It shapes only a Fluent ID's operation: an External wallet's operation ignores a valid policy. - Errors:
execute()rejects withFluentUserOperationGasError, whosecodeis aFluentUserOperationGasErrorCode(errors.md, api.md). AskipEstimationpolicy of the wrong shape isinvalid_policy. Two more checks of askipEstimationpolicy run at submission, and only when a paymaster pays: only one of the two paymaster limits isinvalid_policy, and a paymaster authorization that does not match the fixed limits ispaymaster_gas_mismatch. For a Gas token payment they reject. Under Sponsorship they fail the sponsored submission instead, and the operation falls back to the account's own gas (guides/sponsorship.md). An invalidcallGasLimitorcallGasBufferwithoutskipEstimationrejects with a plainError. Keep adefaultbranch in anyswitchover the code.
This component adds a fifth to the estimate and logs where the time went:
import { useFluentWidget } from "@fluent.xyz/connect";
import { parseEther, type Address } from "viem";
const RECIPIENT: Address = "0x0000000000000000000000000000000000000001";
export function TipWithHeadroom() {
const { widget, status } = useFluentWidget();
if (status !== "connected") return null;
const tip = async () => {
const result = await widget
.createBatchOp({ calls: [{ to: RECIPIENT, value: parseEther("0.001"), data: "0x" }] })
.execute({ userOperationGas: { callGasBuffer: { percentage: 20 } } });
console.log(result.timing?.totalMs, result.paymaster);
};
return <button onClick={() => void tip()}>Tip 0.001 ETH</button>;
}
paymaster and timing
Two members of FluentExecuteResult, what execute() resolves with
(api.md).
paymaster is the address of the contract that paid the operation's gas, read off a Fluent ID's
receipt: the Sponsorship paymaster or a Gas token's paymaster.
- Absent: always for an External wallet, whose
sponsoredis alwaysfalse. For a Fluent ID, whenever the receipt names no paymaster, which includes an operation the account paid for itself;sponsoredis thenfalse, ornullafter a sponsored submission. - When to use it: to log or check which contract paid. To know whether Sponsorship paid,
read the kept
sponsoredmember instead.
timing is a FluentExecuteTiming: where one operation's milliseconds went
(api.md).
prepareMs,signMsandbroadcastMsare measured on the page's clock, and each is absent when the widget could not measure it.retryMsis the time spent on submissions that did not settle, such as a refused Sponsorship before the fallback, and is0for an operation submitted once.inclusionMsanddetectMsare signed estimates across the page's clock and the chain's, so either can be negative when it is small. Both are absent when the including block could not be read.totalMsruns from the first submission to the receipt. It is measured, not summed, and does not equal the sum of the parts.instants, aFluentExecuteTimingInstants, keeps the epoch-millisecond instants and the number ofattemptsthe estimates were computed from (api.md).- Absent: always for an External wallet, whose wallet does its prompt, signing and broadcast in one call; and for a Fluent ID when the page's clock could not be read.
- When to use it: for logs and latency dashboards. It is collected for every Fluent ID
operation, whatever
debugLoggingis set to.
gasPayment.ethValueByToken
FluentWidgetConfig.gasPayment.ethValueByToken takes a FluentGasPaymentEthRates: what one
unit of each Gas token is worth in ETH, as a decimal string, keyed by its FluentGasTokenSymbol
(api.md).
- What it does: sets the rates behind the cost the widget shows beside each Gas token.
- Default: the widget works the rates out from the USD prices it reads for the Gas tokens,
where it has them, with ETH at
"1". A rate set here wins over the derived one for its symbol; the others stay derived. The defaults are the recommended setting. - When to use it: when the costs the widget shows for a Gas token are wrong for your App.
- Applies to: a Fluent ID, the only account kind that pays in a Gas token. An External wallet pays its gas in ETH.
customOAuthRedirectUrl
FluentWidgetConfig.customOAuthRedirectUrl is passed to the provider behind Direct login as
its OAuth redirect URL (api.md). The widget reads it nowhere else.
- Default: omitted, no redirect URL is passed.
- When to use it: in a Capacitor or other native app, where an OAuth return has to land on a Universal Link that the host app handles.
- Requirements: an absolute HTTPS URL, with no credentials, query string or fragment.
Anything else makes
<FluentWidget>throwFluentWidgetConfigErrorwithcodeinvalid_option(errors.md); a blank string counts as omitted. The exact URL must also be registered with Fluent among the allowed redirects of the App's login client. - Applies to: the OAuth flows of the provider behind Direct login. Which of them return to this URL is the provider's to decide, not the widget's.
swapper
FluentWidgetConfig.swapper sets up the on-ramp that the account drawer's Get USDnr button
opens: enabled, integratorId, dstChainId and dstTokenAddress
(api.md).
- Default:
enabledistrue, and the other three are Fluent's own: the on-ramp buys USDnr on Fluent mainnet. The defaults are the recommended setting. - When to use it:
enabled: falseturns the on-ramp off; Get USDnr is then disabled, with "Not configured for this app" on hover. - Applies to: a Fluent ID on mainnet. The on-ramp delivers to the Fluent ID's address, so
Get USDnr is disabled while no Fluent ID is connected. On testnet, Get USDnr mints testnet
USDnr instead and
swapperis not read.
sponsorshipUrl
FluentWidgetConfig.sponsorshipUrl points the widget at a sponsorship service other than the
Network's own (api.md).
- Default: the deployed sponsorship service of the configured Network.
- When to use it: local development of the sponsorship service only. Every real integration uses the default.
- Applies to: Sponsorship of a Fluent ID's operations. An External wallet is never sponsored.
privyClientId
FluentWidgetConfig.privyClientId is the login client Fluent issues for the App, a client-…
value (api.md). It carries the origins allow-listed for Direct
login; appId is what names the App for Sponsorship, Auth tokens and analytics.
- Default: none. It is required today although it is marked experimental: it exists while login runs on the current provider, and goes when Fluent's own login replaces it.
- Errors:
<FluentWidget>throwsFluentWidgetConfigErrorwithcodemissing_optionwhen it is absent or empty, andinvalid_optionwhen it does not start withclient-, anapp_…id included (errors.md). - Applies to: every App, whatever account kind its users connect.