Skip to content

Add unsafeLovelaceValueOf: a positional lovelace accessor for ledger Values#7838

Merged
Unisay merged 2 commits into
masterfrom
yura/issue-2304-positional-lovelace
Jul 13, 2026
Merged

Add unsafeLovelaceValueOf: a positional lovelace accessor for ledger Values#7838
Unisay merged 2 commits into
masterfrom
yura/issue-2304-positional-lovelace

Conversation

@Unisay

@Unisay Unisay commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

unsafeLovelaceValueOf assumes that the first token of the first currency in the Value represents lovelace. Returns a silently wrong answer or fails if that's not the case. It is thus much faster than lovelaceValueOf.

Impact

Isolated budget goldens on a ledger-shaped value (ada first, then three native assets), the same input fed to both functions:

Data lovelaceValueOf Data unsafeLovelaceValueOf delta
CPU 1,295,411 595,767 -54.0%
Memory 7,430 2,628 -64.6%
Scott lovelaceValueOf Scott unsafeLovelaceValueOf delta
CPU 1,657,760 1,232,100 -25.7%
Memory 10,102 7,800 -22.8%

Both variants evaluate to the same result. Because both already short-circuit on the ada-first entry, the delta is purely the removed key extraction, comparisons, and lookup recursion, so it is a fixed saving independent of how many other assets the value carries. A validator that reads lovelace on every input and every output, a common shape, compounds it.

Coverage

A property test in both Spec.V1.Value and Spec.V1.Data.Value checks that on a canonical ledger-shaped value unsafeLovelaceValueOf agrees with lovelaceValueOf and returns the exact lovelace amount. The budget numbers above come from new goldenEvalCekCatchBudget goldens; I also added PIR-readable goldens so the structural difference is visible in review. Goldens are committed for GHC 9.6 and 9.12.

Implements IntersectMBO/plutus-private#2304.

…lues

lovelaceValueOf reads the ada quantity via valueOf, which walks the outer map
comparing each currency-symbol key against adaSymbol and then the inner map
comparing each token-name key against adaToken. For a canonical ledger-provided
Value (a TxOut value) ada sorts first and is always present, so the lovelace
quantity is exactly the first-of-first amount and can be read positionally,
skipping both equalsByteString comparisons and the key unwrapping.

unsafeLovelaceValueOf provides that positional read in both
PlutusLedgerApi.V1.Value (Scott-encoded) and PlutusLedgerApi.V1.Data.Value
(Data-backed), re-exported through PlutusLedgerApi.V1. It is not a drop-in
replacement for lovelaceValueOf: the ada-first-and-present invariant holds only
for ledger-provided values, not for mint or user-constructed Values, where it
would misread or fail. The precondition is documented in the Haddock.

Isolated budget goldens on a ledger-shaped value show -54% CPU / -65% memory for
the Data-backed variant and -26% CPU / -23% memory for the Scott variant, both
evaluating to the same result. Adds property tests asserting agreement with
lovelaceValueOf, plus budget and PIR-readable goldens for GHC 9.6 and 9.12.
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Execution Budget Golden Diff

3a155a8 (master) vs d5544a1

output

This comment will get updated when changes are made.

@Unisay Unisay self-assigned this Jul 10, 2026
@Unisay Unisay requested a review from Copilot July 10, 2026 11:04
@Unisay Unisay marked this pull request as ready for review July 10, 2026 11:05

This comment was marked as low quality.

@zliu41 zliu41 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Was this requested or suggested by someone or was it your own idea?

I shortened the PR description for you. Your version used too many words to say something that can be said very simply.

Comment thread plutus-ledger-api/src/PlutusLedgerApi/V1/Data/Value.hs Outdated
Comment thread plutus-ledger-api/src/PlutusLedgerApi/V1/Value.hs Outdated
@Unisay

Unisay commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Was this requested or suggested by someone or was it your own idea?

The idea I got while optimizing UPLC-CAPE validators and comparing Plinth/Scalus implementations. I saw that "unsafe" Lovelace retrieval gives quite some ex budget boost, especially when its done multiple times (for different tx inputs).

@Unisay Unisay requested a review from zliu41 July 13, 2026 12:09

@zliu41 zliu41 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The idea I got while optimizing UPLC-CAPE validators and comparing Plinth/Scalus implementations. I saw that "unsafe" Lovelace retrieval gives quite some ex budget boost, especially when its done multiple times (for different tx inputs).

Very nice.

@Unisay Unisay merged commit 7f86c43 into master Jul 13, 2026
9 checks passed
@Unisay Unisay deleted the yura/issue-2304-positional-lovelace branch July 13, 2026 17:08
Unisay added a commit that referenced this pull request Jul 13, 2026
PlutusLedgerApi.V2, V3, and Data.V1/V2/V3 now re-export the same Value API that
PlutusLedgerApi.V1 already exposed: the read accessors (valueOf, lovelaceValueOf,
unsafeLovelaceValueOf, currencySymbolValueOf, assetClassValueOf), the AssetClass
type and its constructors, and the helpers flattenValue, symbols, isZero, split,
scale, lovelaceValue, currencySymbol, tokenName. Previously these umbrella
modules exposed only the Value type and a few constructors, so reading a value
from a V2/V3 or Data.* context needed a separate import from V1.Value or
V1.Data.Value. There is no per-version Value type (V2 re-exports V1.Value, V3
re-exports V2.Value, and the Data.* chain mirrors that), so the accessors apply
unchanged.

Widening the re-exports made three explicit imports redundant under
-Werror=unused-imports; removed them: import ...V1.Value (valueOf) in
cardano-loans, and the whole-module ...V1.Data.Value imports in script-contexts
V1.Data and V2.Data ScriptContexts.

Stacked on the unsafeLovelaceValueOf branch (#7838), which adds the accessor
this widening re-exports.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants