dynamic_sdk_fireblocks_flow 1.17.0
dynamic_sdk_fireblocks_flow: ^1.17.0 copied to clipboard
Dynamic SDK Fireblocks Flow API client (attach source, quote, prepare signing, broadcast, flow status).
Dynamic SDK Fireblocks Flow #
Client-side implementation of the Dynamic Fireblocks Flow API —
drives a payment/deposit/withdraw flow through its client-side steps:
attachSource, getQuote, prepareSigning, broadcast, and getFlow.
The Flow session token (dft_...) returned by attachSource is persisted
automatically to secure storage (flutter_secure_storage), keyed by flow ID,
and reused for every subsequent call on that flow — including after the app
is killed and restarted.
Deliberately out of scope:
- Create Flow (
POST /server/{environmentId}/flow/{mode}) requires anAuthorization: Bearer dyn_...API token withflow.writescope. That's a backend-only credential — create the flow from your server, then hand the resultingflowIdto the client. - Sign and Broadcast (step 5) isn't an API call. Sign the
signingPayloadreturned byprepareSigningwith the source wallet's own signer (see thedynamic_sdk/dynamic_sdk_web3dart/dynamic_sdk_solanapackages), submit it to the chain yourself, then report the resulting hash viabroadcast.
Full Example #
1. Initialize the SDK #
// main.dart
import 'package:dynamic_sdk/dynamic_sdk.dart';
import 'package:flutter/material.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
DynamicSDK.init(
props: ClientProps(
environmentId: 'YOUR_ENVIRONMENT_ID',
appName: 'Your App Name',
redirectUrl: 'yourapp://',
),
);
runApp(const MyApp());
}
2. Drive a flow created by your backend #
Your backend creates the flow (POST /server/{environmentId}/flow/{mode})
and gives the client a flowId. From there:
import 'package:dynamic_sdk/dynamic_sdk.dart';
import 'package:dynamic_sdk_fireblocks_flow/dynamic_sdk_fireblocks_flow.dart';
Future<void> runFlow(String flowId, BaseWallet wallet) async {
final flow = DynamicSDK.instance.fireblocksFlow;
// Step 2 -- attach the paying wallet. The session token this returns is
// persisted to secure storage automatically; you never handle it directly.
final attached = await flow.attachSource(
flowId: flowId,
request: AttachSourceRequest(
sourceType: FlowSourceType.wallet,
fromAddress: wallet.address,
fromChainId: '1',
fromChainName: ChainEnum.evm,
),
);
// Step 3 -- get a quote. Quotes expire 60 seconds after issuance.
final quoted = await flow.getQuote(
flowId: flowId,
request: const GetQuoteRequest(fromTokenAddress: '0x...'),
);
// Step 4 -- prepare the unsigned payload for the source chain.
final prepared = await flow.prepareSigning(flowId: flowId);
// Step 5 -- sign `prepared.signingPayload` with the source wallet's own
// signer and broadcast it to the chain yourself (this package does not do
// this step). For an EVM source, for example, map the fields of
// `prepared.signingPayload.evmTransaction` onto a web3dart `Transaction`
// and send it with `DynamicSDK.instance.web3dart.sendTransaction(...)`;
// for SOL/SUI, sign+send `serializedTransaction` with `dynamic_sdk_solana`
// (or the SUI signer); for BTC, sign the `psbt`.
// `signAndBroadcast` is your own app code -- not part of this package.
final txHash = await signAndBroadcast(prepared.signingPayload, wallet);
// Step 6 -- report the resulting transaction hash.
await flow.broadcast(
flowId: flowId,
request: BroadcastRequest(txHash: txHash),
);
// Step 7 -- poll until settled. This call needs no session token, so it
// also works after an app restart to resume tracking an in-flight flow.
var status = await flow.getFlow(flowId: flowId);
while (status.settlementState != FlowSettlementState.completed &&
status.settlementState != FlowSettlementState.failed) {
await Future.delayed(const Duration(seconds: 3));
status = await flow.getFlow(flowId: flowId);
}
}
3. Resuming after an app restart #
Because the session token is persisted per flowId, tracking an in-flight
flow after the app was killed just means calling getFlow() again — no
re-attach needed unless the token has actually expired:
final status = await DynamicSDK.instance.fireblocksFlow.getFlow(flowId: flowId);
If a later call (getQuote, prepareSigning, broadcast) throws a
FireblocksFlowException with sessionTokenInvalid == true, the stored token
has already been removed — call attachSource() again to obtain a new one.