Skip to content

Display better fee breakdown for soroban transactions#774

Open
leofelix077 wants to merge 11 commits intomainfrom
lf-improve-fee-display-on-send
Open

Display better fee breakdown for soroban transactions#774
leofelix077 wants to merge 11 commits intomainfrom
lf-improve-fee-display-on-send

Conversation

@leofelix077
Copy link
Collaborator

@leofelix077 leofelix077 commented Mar 18, 2026

Already verified UI with @sdfcharles on build 1.13.25 (1774353202)

Screenshot 2026-03-24 at 12 15 13 Screenshot 2026-03-24 at 11 50 24 Screenshot 2026-03-24 at 11 50 29 Screenshot 2026-03-24 at 11 50 35 Screenshot 2026-03-24 at 11 50 44

Checklist

PR structure

  • This PR does not mix refactoring changes with feature changes (break it down into smaller PRs if not).
  • This PR has reasonably narrow scope (break it down into smaller PRs if not).
  • This PR includes relevant before and after screenshots/videos highlighting these changes.
  • I took the time to review my own PR.

Testing

  • These changes have been tested and confirmed to work as intended on Android.
  • These changes have been tested and confirmed to work as intended on iOS.
  • These changes have been tested and confirmed to work as intended on small iOS screens.
  • These changes have been tested and confirmed to work as intended on small Android screens.
  • I have tried to break these changes while extensively testing them.
  • This PR adds tests for the new functionality or fixes.

Release

  • This is not a breaking change.
  • This PR updates existing JSDocs when applicable.
  • This PR adds JSDocs to new functionalities.
  • I've checked with the product team if we should add metrics to these changes.
  • I've shared relevant before and after screenshots/videos highlighting these changes with the design team and they've approved the changes.
@leofelix077 leofelix077 self-assigned this Mar 18, 2026
Copilot AI review requested due to automatic review settings March 18, 2026 21:40
@leofelix077 leofelix077 changed the title add initial version of better resource and inclusion fees display [WIP] add initial version of better resource and inclusion fees display Mar 18, 2026
@leofelix077 leofelix077 added wip work in progress don't review yet Work in Progress / Draft PR / Code Review adjustments being worked on labels Mar 18, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an initial UI/UX for displaying a clearer fee breakdown (inclusion vs resource fees) for Soroban transactions, by plumbing Soroban simulation fee data from the backend through transaction building and into new bottom sheet UI.

Changes:

  • Extend Soroban simulation plumbing to surface minResourceFee alongside prepared transaction XDR.
  • Add a new FeeBreakdownBottomSheet and hook it up from send review/settings flows.
  • Update transaction builder state + related tests/translations to support inclusion/resource/total fee display.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/services/transactionService.ts Returns { preparedTransaction, minResourceFee } from Soroban simulation wrappers.
src/services/backend.ts Types simulationResponse and introduces SorobanSimulationResponse with minResourceFee.
src/ducks/transactionBuilder.ts Stores computed Soroban inclusion/resource fees (XLM) in Zustand state.
src/components/screens/SendScreen/screens/TransactionAmountScreen.tsx Auto-builds for fee estimation and wires up fee breakdown bottom sheet.
src/components/screens/SendScreen/components/SendReviewBottomSheet.tsx Displays total fee (classic vs Soroban total) and opens breakdown sheet.
src/components/TransactionSettingsBottomSheet.tsx Updates fee label/info behavior to support “inclusion fee” and fee breakdown entry point.
src/components/FeeBreakdownBottomSheet.tsx New bottom sheet UI showing fee rows + contextual description.
src/i18n/locales/en/translations.json Adds fee breakdown and fee row labels (EN).
src/i18n/locales/pt/translations.json Adds fee breakdown and fee row labels (PT).
tests/services/transactionService.test.ts Updates simulate collectible test for new return shape.
tests/ducks/transactionBuilder.test.ts Updates mocks for new simulate return shape.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +294 to +297
expect(result).toEqual({
preparedTransaction: mockPreparedXdr,
minResourceFee: undefined,
});
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an initial “better fees” UI by separating Soroban inclusion vs resource fees and plumbing minResourceFee from simulation responses through the transaction build flow.

Changes:

  • Plumbs Soroban minResourceFee through backend/service simulation calls and stores it in the transaction builder state.
  • Updates Send flows to pre-simulate Soroban transactions (including “amount=0” fee estimation) and adds a Fee Breakdown bottom sheet.
  • Updates fee-related labels/translations and adjusts tests for the new simulation return shape.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/services/transactionService.ts Adds optional amount validation skip for Soroban fee estimation; returns { preparedTransaction, minResourceFee } from simulation helpers
src/services/backend.ts Updates simulation response typing and changes params.amount type to number
src/ducks/transactionBuilder.ts Stores Soroban fee breakdown fields and uses simulation results to compute/display fees
src/components/screens/SendScreen/screens/TransactionAmountScreen.tsx Adds auto fee-estimation simulation for Soroban and wires Fee Breakdown sheet
src/components/screens/SendScreen/screens/SendCollectibleReview.tsx Auto-simulates to populate fee breakdown; wires Fee Breakdown sheet
src/components/screens/SendScreen/components/SendReviewBottomSheet.tsx Shows total fee (classic or inclusion+resource) and opens Fee Breakdown sheet
src/components/TransactionSettingsBottomSheet.tsx Renames fee label to “Inclusion fee” for Soroban and routes info icon to Fee Breakdown
src/components/FeeBreakdownBottomSheet.tsx New UI component for inclusion/resource/total fee breakdown
src/i18n/locales/en/translations.json Adds strings for inclusion/resource/total fee breakdown
src/i18n/locales/pt/translations.json Adds Portuguese strings for inclusion/resource/total fee breakdown
tests/services/transactionService.test.ts Updates simulation tests for new return shape and minResourceFee plumbing
tests/ducks/transactionBuilder.test.ts Updates mocks/assertions for new simulation return shape and fee fields
tests/components/TransactionSettingsBottomSheet.test.tsx Mocks transaction builder Soroban state for updated component logic

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +655 to +656
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedBalance?.id, recipientAddress]);
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This effect gates on !isBuilding, but isBuilding is not in the dependency array. If the effect runs while a build is in progress, the fee-estimation simulation will never be scheduled when isBuilding flips back to false. Include isBuilding (and prepareTransaction) in the dependencies or restructure so the fee-estimation trigger re-evaluates when building completes.

Suggested change
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedBalance?.id, recipientAddress]);
}, [selectedBalance?.id, recipientAddress, isBuilding, prepareTransaction]);
Copilot uses AI. Check for mistakes.
Comment on lines +116 to +118
// Single source of truth for Soroban state from the transaction builder store
const { isSoroban: isSorobanTransaction } = useTransactionBuilderStore();

Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TransactionSettingsBottomSheet now treats useTransactionBuilderStore().isSoroban as the single source of truth. That value can be stale in contexts that don’t rebuild via buildTransaction (e.g., swap settings) or before any build has started, leading to incorrect fee labeling and memo/contractId logic. Consider deriving Soroban-ness from the current settings/context (selected token, recipient, collectible) here, or ensure the builder store is always reset/updated for every context that can open this sheet.

Copilot uses AI. Check for mistakes.
@leofelix077 leofelix077 added don't review yet Work in Progress / Draft PR / Code Review adjustments being worked on and removed wip work in progress don't review yet Work in Progress / Draft PR / Code Review adjustments being worked on labels Mar 24, 2026
@leofelix077 leofelix077 changed the title [WIP] add initial version of better resource and inclusion fees display Display better fee breakdown for soroban transactions Mar 24, 2026
@leofelix077 leofelix077 added enhancement New feature or request and removed don't review yet Work in Progress / Draft PR / Code Review adjustments being worked on labels Mar 24, 2026
@leofelix077 leofelix077 requested a review from Copilot March 24, 2026 16:48
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 534 to 537
<Button
tertiary={(isSuspicious && !isUnableToScan) || isExpectedToFail}
destructive={isMalicious && !shouldUseRowLayout}
tertiary={isSuspicious || isUnableToScan || isExpectedToFail}
destructive={isMalicious}
secondary={shouldUseRowLayout}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the cancel button styling logic (tertiary/destructive) in a way that appears unrelated to the PR’s stated goal (fee breakdown for Soroban transactions). If this behavior change is intentional, it would be good to call it out in the PR description; otherwise consider reverting it to keep the PR focused.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intentional change

Screenshot 2026-03-24 at 08 51 06 Screenshot 2026-03-24 at 08 50 53
Comment on lines +76 to +103
{/* Fee rows card */}
<View className="mt-[16px] rounded-[12px] overflow-hidden bg-background-tertiary">
{isSoroban && (
<View className="flex-row justify-between items-center px-[16px] py-[12px] border-b border-gray-6">
<Text md secondary>
{t("transactionAmountScreen.details.inclusionFee")}
</Text>
{isBuilding || !sorobanInclusionFeeXlm ? (
<ActivityIndicator
size="small"
color={themeColors.text.secondary}
/>
) : (
<Text md primary>
{formatTokenForDisplay(
sorobanInclusionFeeXlm,
NATIVE_TOKEN_CODE,
)}
</Text>
)}
</View>
)}
{isSoroban && (
<View className="flex-row justify-between items-center px-[16px] py-[12px] border-b border-gray-6">
<Text md secondary>
{t("transactionAmountScreen.details.resourceFee")}
</Text>
{isBuilding || !sorobanResourceFeeXlm ? (
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isSorobanContext is documented as controlling row visibility independently of the builder store, but the component actually gates the Inclusion/Resource fee rows (and totalFeeXlm) on isSoroban from useTransactionBuilderStore(). This can hide Soroban rows when opening the sheet before a build has set isSoroban (or if the store is stale). Consider using isSorobanContext to control row visibility (and possibly the total-fee calculation), and use the store values only for the amounts/spinner states.

Copilot uses AI. Check for mistakes.
});
const isFeeEstimation = Number(params.tokenAmount) === 0;

let simulateResult;
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let simulateResult; leaves simulateResult implicitly typed as any, which defeats type-safety and may violate TypeScript/ESLint settings. Prefer giving it an explicit union type (e.g., the shared shape { preparedTransaction: string; minResourceFee?: string }) or restructuring to avoid the untyped variable.

Suggested change
let simulateResult;
let simulateResult: { preparedTransaction: string; minResourceFee?: string };
Copilot uses AI. Check for mistakes.
Comment on lines +232 to +236
amount:
builtTxResult.amountInBaseUnits !== undefined
? String(builtTxResult.amountInBaseUnits)
: "0",
},
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When building the /simulate-token-transfer request, the amount falls back to the string "0" if builtTxResult.amountInBaseUnits is undefined. That would silently simulate the wrong transaction rather than failing fast. Since amount is required by the backend contract, consider throwing an explicit error (or deriving base units from the entered amount) instead of defaulting to "0".

Copilot uses AI. Check for mistakes.
Comment on lines +417 to +419
const sorobanResourceFeeXlm = simulateResult.minResourceFee
? stroopToXlm(new BigNumber(simulateResult.minResourceFee)).toFixed(7)
: null;
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorobanResourceFeeXlm is derived from simulateResult.minResourceFee without validating that the value is numeric. If the backend ever returns a non-numeric string, this can propagate "NaN" into the UI. Consider mirroring the earlier isNaN() guard used in buildTransaction before converting/formatting the fee.

Suggested change
const sorobanResourceFeeXlm = simulateResult.minResourceFee
? stroopToXlm(new BigNumber(simulateResult.minResourceFee)).toFixed(7)
: null;
const sorobanResourceFeeXlm =
simulateResult.minResourceFee &&
!Number.isNaN(Number(simulateResult.minResourceFee))
? stroopToXlm(
new BigNumber(simulateResult.minResourceFee),
).toFixed(7)
: null;
Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

2 participants