DEV Community

WingsDevelopment
WingsDevelopment

Posted on

The Underestimated Problem Silently Draining Trust From Your DeFi App

Banner

A lot has changed recently.

Institutional players are actively exploring DeFi. Risk frameworks are becoming table stakes. At Euler, we are moving toward institutional-grade infrastructure — and we are not alone. More and more projects across the ecosystem are getting serious.

At the same time, AI has fundamentally changed what is possible for small teams.

Writing code is no longer the bottleneck. Knowledge is.

Any team that takes its craft seriously can now achieve the kind of engineering rigor that was once reserved for big corporations.

With all of this in mind, the goal is clear:

We need to raise the status quo and build a frontend that sets the standard for any financial application.

This post is about one of the most underrated problems standing in the way.


Data Formatting and Error Propagation

This might not sound exciting, but hear me out.

The single most underrated problem in any DeFi frontend is data formatting and the propagation of errors and warnings to the UI.

The goal sounds simple:

We should always provide the user with correct and maximum information, with all errors and warnings shown explicitly, right where they are needed — zoomed in to the specific numbers that are affected.

In practice, this is incredibly hard.

DeFi apps fetch data from many different sources:

  • Price APIs
  • Third-party services
  • Indexers
  • Backends
  • On-chain calls
  • Random tokens
  • Different vaults
  • An ever-growing number of chains

Any data point can arrive as an unexpected type, a null, a stringified bigint, or simply be missing — while documentation promised to never return undefined.

We cannot trust external types. We always need to be paranoid.

And when something goes wrong?

Your whole page or section breaks.

That is better than a silent crash, sure. But it is not good enough.

If only one number on the entire page is problematic, only that number should show an error — not the whole table.


Common Mistakes

I have reviewed a lot of projects and codebases. These patterns show up everywhere, including in many well-known projects.


Formatting Into Strings Instead of Objects

We have all been there.

Your designer drops by:

"Hey, can we make the dollar sign gray and add 4px of spacing between it and the number?"

But you already formatted your number into a string like this:

"$2,423.00"
Enter fullscreen mode Exit fullscreen mode

So what can you do now?

Formatted string problem

Once the value is flattened into a string, the UI loses flexibility.

You now need to split strings, use regex, or rewrite formatting logic just to style different parts of the value.


Trusting External Interfaces

Types say number, but reality sends:

  • undefined
  • null
  • A stringified number
  • A malformed value
  • A missing field

A classic example is the famous hardcoded fallback:

decimals || 18
Enter fullscreen mode Exit fullscreen mode

It works — until it does not.

Silent coercion feels convenient, but in financial interfaces it can easily become dangerous.


Silent Errors Everywhere

This happens constantly:

  • 0 renders instead of undefined
  • Missing data shows as an empty <span>
  • Console errors are logged but never surfaced to the user
  • Warnings are swallowed somewhere deep in the data layer
  • The UI looks fine, but the value is wrong or incomplete

In DeFi, silent errors are especially dangerous because users often make financial decisions based on what the interface shows them.


All-or-Nothing Error Handling

One bad data point should not take down an entire section or page.

If one value is broken, that specific value should gracefully degrade.

Instead of this:

Entire table failed to render
Enter fullscreen mode Exit fullscreen mode

We want this:

Only the problematic value shows an error or warning
Enter fullscreen mode Exit fullscreen mode

The rest of the page should remain usable.

I have seen this pattern over and over.

And I think we can do much better.


How It Should Work

Instead of formatting numbers into flat strings, we should return structured objects that carry:

  • The formatted value
  • Metadata about the value
  • Warnings encountered during formatting
  • Errors encountered during formatting

For example:

export interface RobustFormattingResult<T> {
  value: T | undefined
  warnings: string[]
  errors: string[]
}
Enter fullscreen mode Exit fullscreen mode

The value itself should be a rich type like ViewNumber.

Not a string.

An object with everything the UI needs:

export type ViewNumber = {
  belowMin?: boolean
  aboveMax?: boolean
  symbol?: string
  originalValue?: string
  compact?: string
  viewValue?: string
  sign?: string
  decimals?: number
}
Enter fullscreen mode Exit fullscreen mode

This gives the UI full control.

A component can render:

  • sign + symbol + viewValue
  • symbol + sign + viewValue
  • Compact notation
  • Full notation
  • Below-min values like <$0.01
  • Above-max values like >$100
  • Symbols with custom styling
  • Warnings and errors next to the affected value

And it can do all of that without reformatting anything.


Why This Matters

When combined with warnings and errors, every number on the page can independently communicate its own trustworthiness.

For example:

Missing price from backend — unable to calculate this value.
Enter fullscreen mode Exit fullscreen mode

Or:

Automatically converted type — please double check this information.
Enter fullscreen mode Exit fullscreen mode

The user always gets maximum transparency.

Instead of hiding uncertainty, the interface shows exactly what happened and where.

That is the difference between a frontend that merely renders data and a frontend that users can trust.


Rendering It

Once you have a RobustFormattingResult, rendering becomes straightforward.

You format the value once:

const formattedResult = robustFormatBigIntToViewTokenAmount({
  context: `${STORY_CONTEXT}.Playground`,
  input: {
    bigIntValue: 123456789n,
    decimals: 6,
    symbol: "USDC",
  },
})
Enter fullscreen mode Exit fullscreen mode

Then you pass the result directly into a display component:

return (
  <h5 className="text-2xl font-semibold leading-none">
    <DisplayTokenAmountField {...args} {...formattedResult} />
  </h5>
)
Enter fullscreen mode Exit fullscreen mode

Under the hood, everything renders as simple <span> elements:

<span class="inline-flex items-center">
  <span class="inline tracking-tight">123.45</span>
  <span class="inline items-center text-slate-300">USDC</span>
</span>
Enter fullscreen mode Exit fullscreen mode

This is intentional.

Every part of the number is its own element:

  • Value
  • Symbol
  • Sign
  • Warnings
  • Errors
  • Loading state
  • Additional metadata

This means you can style any piece independently.

You can set spacing between spans, change colors, inject custom components, or wrap the whole thing in a <Typography> component so everything inherits the correct text styles.

Even skeleton loaders can scale automatically with the surrounding typography.

No fighting with flat strings.

No regex.

No splitting formatted numbers apart after the fact.


See It in Action

Mock Vaults Demo

Mock Vaults Demo

A live page simulating real-world data issues and showing how the UI handles each one transparently, with detailed component overviews.

You can also see the full fetch-mapper-hook architecture in action here:


Storybook — 30+ Cases

Storybook — DisplayTokenAmountField

This Storybook includes over 30 different cases for a single number component, including:

  • Loading states
  • Errors
  • Warnings
  • Edge cases
  • Below-min values
  • Above-max values
  • Compact notation
  • Full notation

Open-Source Libraries

The goal of this post is not to promote one specific implementation.

It is about changing how we think about the problem.

Once you start treating every number as a structured result with errors and warnings attached, the implementation follows naturally.

AI can help you build custom solutions tailored to your needs.

Or, if these libraries fit your use case, they are designed to support flexible UI implementations out of the box.


web3-robust-formatting

web3-robust-formatting

Formatters and mappers built around the structured result interface described above.


web3-display-components

web3-display-components

React components with support for:

  • Loading states
  • Warnings
  • Errors
  • Compound values
  • Tooltips
  • Flexible rendering
  • Everything rendered through <span> elements for easy styling

AI Skills

The libraries also come with AI skills.

AI can initialize wrapper components for your project and learn when to use the libraries properly as you build.

CODEX_HOME="$(pwd)/.codex" npx web3-robust-formatting-codex-skill install --force
npx web3-display-components-codex-skill init-agents
Enter fullscreen mode Exit fullscreen mode
npx web3-robust-formatting-codex-skill install
npx web3-robust-formatting-codex-skill init-agents
Enter fullscreen mode Exit fullscreen mode

Conclusion

Knowledge is power.

Understanding how to think about data in financial systems is what separates a great frontend from a mediocre one.

The UI's main job is simple:

Do not break the user's trust.

Shifting things around and making things prettier will not attract that many users by itself.

But wrong values, missing warnings, silent failures, and pages that break?

That drives users away, frustrates them, and erodes trust — sometimes permanently.

Stop underestimating this problem.

Start treating every number in your UI as a promise to the user.

Let's make DeFi great together.

Top comments (0)