resolveGasData method
One-shot server-side gas resolution for a fully input-resolved
transaction: the server selects the gas coin and computes the budget
(gRPC simulate with doGasSelection). Returns null when the transport
does not support it (e.g. JSON-RPC); the builder then falls back to local
coin selection + a budget dry-run.
Implementation
@override
Future<ResolvedGasData?> resolveGasData(
TransactionBlockDataBuilder data) async {
// Structured message (not BCS, which cannot express an unset budget) +
// `doGasSelection` — mirrors Mysten's TS gRPC `resolveTransactionPlugin`.
// Omit any preset (e.g. dApp-supplied) budget so the node estimates it
// rather than selecting coins to satisfy that budget.
final grpcTx = transactionDataToGrpcTransaction(data, includeBudget: false);
final sim = await core.simulateStructured(
grpcTx,
doGasSelection: true,
readMask: const [
'transaction.transaction.gas_payment',
'transaction.effects.status',
],
);
final status = sim.transaction.effects.status;
if (!status.success) {
throw StateError(
'Gas resolution simulation failed: ${status.error.description}');
}
final gp = sim.transaction.transaction.gasPayment;
final budget = BigInt.parse(gp.budget.toString());
if (gp.objects.isEmpty) {
// The node selected no gas coin (an address-balance PTB it deems
// gasless-capable). Executing without a gas coin additionally requires a
// ValidDuring expiration, which this resolution path does not manage —
// gasless sends are built explicitly via `SuiGrpcCompat.setGaslessGas`.
// Fall back to local gas selection here so the transaction stays valid.
return null;
}
return ResolvedGasData(
budget: budget,
price: gp.hasPrice() ? BigInt.parse(gp.price.toString()) : null,
payment: gp.objects
.map((o) => SuiObjectRef(o.digest, o.objectId, o.version.toInt()))
.toList(),
);
}