Skip to content

Comments

Payment testing infrastructure improvements#2282

Merged
andrei-marinica merged 24 commits intorc/v0.65from
test-payment
Feb 11, 2026
Merged

Payment testing infrastructure improvements#2282
andrei-marinica merged 24 commits intorc/v0.65from
test-payment

Conversation

@andrei-marinica
Copy link
Contributor

@andrei-marinica andrei-marinica commented Feb 10, 2026

PR Summary: Payment System Unification and Type Safety Improvements

Target Branch: rc/v0.65

Overview

This PR introduces a unified payment syntax for transaction building, enhances type safety across payment types, and improves the developer experience with better documentation and more convenient APIs.

Major Changes

1. Unified Payment Syntax 🎯

Motivation: Simplify transaction building with a single, composable .payment() method.

New TxPaymentCompose Trait

  • Introduced a trait for composing multiple payments into complex payment structures
  • Enables fluent API: .payment(x).payment(y).payment(z) automatically composes payments
  • Type-safe composition with diagnostic messages for incompatible payment types

Transaction Builder Updates

  • Deprecated: .esdt() and .multi_esdt() methods
  • New: Unified .payment() method that works with all payment types
  • Automatically handles single ESDT, multiple ESDT, EGLD, and mixed payments

Example:

// Old syntax (deprecated)
tx.esdt(payment1).multi_esdt(payment2)

// New unified syntax
tx.payment(payment1).payment(payment2)

2. Payment Type Enhancements 📦

Enhanced Constructors

  • Payment::new<T, A>(token_id, nonce, amount) - Generic with Into conversions
    • Accepts any type convertible to TokenId<M> and NonZeroBigUint<M>
  • Payment::try_new<T, A, E>(token_id, nonce, amount) - Fallible version
    • Returns Result<Payment, E> for zero-amount handling

Triple Tuple Syntax

  • New file: tx_payment_triple.rs
  • Support for (TokenId, u64, Amount) tuples as payments
  • Implements TxPayment and TxPaymentCompose for tuple syntax
  • Enables ergonomic inline payments: .payment(("TOKEN-123", 0, 1000u64))

Comprehensive Documentation

Added extensive documentation including:

  • Detailed type descriptions and usage examples
  • Key features explanations (type safety, NFT/SFT support, etc.)
  • Cross-references to related types

3. NonZeroBigUint Improvements 🔢

New Conversions

  • From NonZero<T> primitives: NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize
    • Type-safe conversion preserving non-zero guarantee
  • TryFrom expanded to all unsigned integer types: u8, u16, u32, u64, u128, usize

New Constructor

  • NonZeroBigUint::one() - Convenient constructor for the common case of value 1

Comparison Operators

  • New file: non_zero_big_uint_cmp.rs
  • Implements PartialEq and PartialOrd between NonZeroBigUint and BigUint
  • Enables direct comparisons without manual conversions

4. EsdtTokenPayment Enhancements 💳

New Conversion Method

  • into_payment() - Converts EsdtTokenPayment to Payment
    • Enforces non-zero amount requirement
    • Panics if amount is zero (use only when amount is guaranteed non-zero)

Type Alias Additions

  • Added EsdtTokenPaymentVec<Api> as preferred alias
  • Marked MultiEsdtPayment<Api> for deprecation (in favor of EsdtTokenPaymentVec)

5. Safety Improvements 🛡️

Removed Unsafe Transmutations

  • Removed: Payment::as_egld_or_esdt_payment() (unsafe transmute)
  • Removed: EsdtTokenPayment::as_egld_or_esdt_payment() (unsafe transmute)
  • Kept: Safe conversion methods like into_egld_or_esdt_payment()

This eliminates potential undefined behavior from type punning while maintaining conversion capabilities.

6. Test API Improvements 🧪

TestTokenIdentifier Updates

  • Deprecated: to_token_identifier() (incorrectly named)
  • New: to_esdt_token_identifier() - for ESDT-only scenarios
  • New: to_token_id() - for general token ID conversion
  • Added comprehensive documentation explaining when to use each method

Test Migration

  • Removed: TestPayment type
  • Deprecated: TestEsdtTransfer
  • Moved: Test utilities to expr module for better organization

7. Contract Updates 🔧

Updated contracts to use the new unified syntax:

  • composability examples and tests
  • forwarder contracts and interactors
  • payable-features tests (95 lines added)
  • Module tests: governance, staking, token-merge
  • Various proxy files regenerated

8. Additional Enhancements

New Test Coverage

  • Added payment_test.rs - comprehensive payment type tests
  • Enhanced non_zero_big_uint_test.rs - additional conversion and comparison tests
  • Added scenario: big_num_ops_cmp.scen.json - big number comparison tests

Documentation

  • Improved inline documentation across payment types
  • Added usage examples to key methods
  • Better diagnostic messages for trait composition failures

Breaking Changes ⚠️

  1. Deprecated APIs:

    • Tx::esdt() - use .payment() instead
    • Tx::multi_esdt() - use .payment() instead
    • TestTokenIdentifier::to_token_identifier() - use .to_token_id() or .to_esdt_token_identifier()
  2. Removed Methods:

    • Payment::as_egld_or_esdt_payment() - unsafe, use .into_egld_or_esdt_payment()
    • EsdtTokenPayment::as_egld_or_esdt_payment() - unsafe method removed
    • EsdtTokenPayment::into_multi_egld_or_esdt_payment() - renamed to into_egld_or_esdt_payment()
  3. Type Changes:

    • Payment::new() and Payment::try_new() are now generic over argument types

Migration Guide

Updating Transaction Builders

// Before
tx.esdt(("TOKEN-abc", 0, amount))
  .multi_esdt(another_payment)

// After
tx.payment(("TOKEN-abc", 0, amount))
  .payment(another_payment)

Updating Payment Conversions

// Before
let egld_or_esdt = payment.as_egld_or_esdt_payment();  // unsafe

// After
let egld_or_esdt = payment.into_egld_or_esdt_payment();  // safe

Updating TestTokenIdentifier Usage

// Before
let token_id = test_token.to_token_identifier();

// After - for ESDT scenarios
let esdt_id = test_token.to_esdt_token_identifier();

// After - for general use
let token_id = test_token.to_token_id();
// or simply
let token_id: TokenId<_> = test_token.into();

Benefits

  1. Improved Type Safety: Removed unsafe operations, stronger guarantees
  2. Better DX: Unified syntax is simpler and more intuitive
  3. More Flexible: Generic constructors accept wider range of types
  4. Better Documented: Extensive inline documentation and examples
  5. Future-Proof: Composable design enables future payment type extensions

Testing

  • All existing tests updated and passing
  • New test files added for payment and NonZeroBigUint functionality
  • Blackbox and whitebox tests updated across feature-test contracts
  • Scenario tests added for new comparison operators

Files Modified by Category

Framework Core (12 files)

  • Payment types and constructors
  • NonZeroBigUint enhancements
  • Transaction builder API
  • Test utilities

Contracts (28 files)

  • Example contracts updated
  • Feature test contracts migrated
  • Contract modules updated
  • Proxy files regenerated

Tests (15 files)

  • New payment tests
  • Enhanced NonZeroBigUint tests
  • Updated integration tests
  • New scenario files

Note: This PR maintains backward compatibility for most use cases through deprecation warnings. Direct breaking changes are limited to unsafe methods that should not be widely used.

@github-actions
Copy link

github-actions bot commented Feb 10, 2026

Contract comparison - from 18fab3e to 01580e2

Path                                                                                             size                  has-allocator                     has-format
fractional-nfts.wasm 8302 false without message
multisig-view.wasm 5590 false None
multisig-full.wasm 15111 false without message
multisig.wasm 13606 false without message
nft-minter.wasm 9726 false without message
ping-pong-egld.wasm 6397 false None
factorial.wasm 579 false None
nft-subscription.wasm 8725 false without message
kitty-auction.wasm 9394 false without message
kitty-genetic-alg.wasm 3494 false without message
kitty-ownership.wasm 12953 false without message
nft-storage-prepay.wasm 2609 false None
crypto-bubbles.wasm 2561 false None
esdt-transfer-with-fee.wasm 7505 false without message
seed-nft-minter.wasm 14189 false without message
adder.wasm 699 false None
bonding-curve-contract.wasm 14011 false None
proxy-pause.wasm 4165 false None
empty.wasm 244 false None
crowdfunding.wasm 3574 false None
digital-cash.wasm 9730 false None
token-release.wasm 6948 false without message
lottery-esdt.wasm 10580 false without message
rewards-distribution.wasm 9449 false without message
check-pause.wasm 1240 false None
order-book-factory.wasm 3401 false None
order-book-pair.wasm 14098 false None
crypto-zombies.wasm 9276 false without message
set-repeat.wasm 6511 false None
single-value-repeat.wasm 4253 false None
map-repeat.wasm 7363 false without message
queue-repeat.wasm 5536 false None
linked-list-repeat.wasm 6838 false without message
vec-repeat.wasm 4872 false None
str-repeat-mb-builder-cached.wasm 1109 false without message
str-repeat-mb-builder-basic.wasm 757 false None
str-repeat.wasm 2733 false without message
large-storage.wasm 1656 false None
send-tx-repeat.wasm 1292 false None
abi-tester.wasm 8607 true without message
abi-tester-ev.wasm 760 false None
exchange-features.wasm 1514 false None
use-module.wasm 32360 false without message
use-module-view.wasm 736 false None
panic-message-features.wasm 12838 false with message
panic-message-std.wasm 15886 false with message
big-float-features.wasm 6373 false without message
scenario-tester.wasm 1374 false None
forbidden-opcodes.wasm 842 false None
basic-features.wasm 85420 ➡️ 85769 🔴 (+349) false without message
basic-features-storage-bytes.wasm 541 false None
payable-features.wasm 6046 false None
std-contract.wasm 3469 true without message
forwarder-queue.wasm 12712 false without message
forwarder-queue-promises.wasm 13336 false without message
forwarder-raw.wasm 13081 false None
forwarder-raw-init-sync-call.wasm 2958 false None
forwarder-raw-init-async-call.wasm 2374 false None
forwarder-legacy.wasm 33262 ➡️ 33533 🔴 (+271) false without message
recursive-caller.wasm 5163 false without message
second-contract.wasm 1158 false None
first-contract.wasm 3450 false None
proxy-test-first.wasm 5711 false without message
transfer-role-features.wasm 8605 false without message
vault.wasm 8857 ➡️ 8950 🔴 (+93) false None
vault-upgrade.wasm 708 false None
forwarder.wasm 48793 ➡️ 49169 🔴 (+376) false without message
proxy-test-second.wasm 2327 false without message
local-esdt-and-nft.wasm 12254 ➡️ 12568 🔴 (+314) false without message
parent.wasm 1999 false None
child.wasm 3982 false without message
builtin-func-features.wasm 3828 false None
rust-snippets-generator-test.wasm 4708 false None
rust-testing-framework-tester.wasm 8584 false None
multi-contract-features-view.wasm 1113 false None
multi-contract-features.wasm 681 false None
multi-contract-alt-impl.wasm 353 false None
multi-contract-example-feature.wasm 680 false None
alloc-mem-leaking.wasm 23325 false without message
alloc-features.wasm 23168 false without message
alloc-mem-fail.wasm 17720 true without message
erc1155-user-mock.wasm 1229 false None
erc721.wasm 2232 false None
crowdfunding-erc20.wasm 4909 false without message
lottery-erc20.wasm 12886 false without message
erc1155.wasm 11969 false without message
erc1155-marketplace.wasm 10603 false without message
erc20.wasm 1887 false None
esdt-system-sc-mock.wasm 4199 ➡️ 4556 🔴 (+357) false None
formatted-message-features.wasm 3613 false without message
multiversx-price-aggregator-sc.wasm 17896 false without message
multiversx-wegld-swap-sc.wasm 4304 false None

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 modernizes and expands the payment construction/testing flow by introducing a composable .payment(...) mechanism in Tx, broadening numeric/token-id conversion support, and updating contracts + tests to use the new APIs.

Changes:

  • Introduces TxPaymentCompose and refactors Tx::payment(...) to support composition (enabling fluent multi-payment building) while deprecating legacy esdt/multi_esdt helpers.
  • Expands conversion ergonomics for NonZeroBigUint and TokenId, and updates proxy generation + send wrappers to use .payment(...).
  • Updates a wide set of contract modules and feature/scenario tests to use Payment::try_new(...) and the new composition pattern.

Reviewed changes

Copilot reviewed 27 out of 28 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
framework/scenario/tests/non_zero_big_uint_test.rs Extends NonZeroBigUint conversion tests (including NonZero<T> sources).
framework/derive/src/generate/proxy_gen.rs Switches generated multi-payment initialization from .multi_esdt(...) to .payment(...).
framework/base/src/types/managed/wrapped/token/token_id.rs Adds additional From<...> conversions for TokenId (incl. EsdtTokenIdentifier, String, TestTokenIdentifier).
framework/base/src/types/managed/wrapped/token/payment.rs Adds extensive Payment docs and introduces Payment::try_new(...) for flexible construction.
framework/base/src/types/managed/wrapped/num/non_zero_big_uint.rs Adds TryFrom for more unsigned ints and From<NonZero<T>> conversions.
framework/base/src/types/managed/wrapped/managed_byte_array.rs Adds TypeAbiFrom coverage for [u8; N] inputs.
framework/base/src/types/interaction/tx_payment/tx_payment_payment.rs Implements TxPayment for Payment and composition into PaymentVec.
framework/base/src/types/interaction/tx_payment.rs Adds the new TxPaymentCompose trait (composition contract).
framework/base/src/types/interaction/tx.rs Refactors .payment(...) to use composition; deprecates legacy ESDT helpers; adds (currently stubbed) id().
framework/base/src/types/interaction/expr/test_token_identifier.rs Adds to_token_id() convenience.
framework/base/src/types/interaction/expr/test_esdt_transfer.rs Deprecates TestEsdtTransfer in favor of Payment::try_new(...).unwrap().
framework/base/src/types/interaction/expr.rs Re-exports TestEsdtTransfer from expr module.
framework/base/src/contract_base/wrappers/send_wrapper.rs Migrates async ESDT send helper to build Payment and call .payment(...).
contracts/modules/src/bonding_curve/utils/user_endpoints.rs Migrates .multi_esdt(...) usage to .payment(...).
contracts/modules/src/bonding_curve/utils/owner_endpoints.rs Migrates .multi_esdt(...) usage to .payment(...).
contracts/feature-tests/use-module/tests/token_merge_module_whitebox_test.rs Updates multi-payment test flows to chained .payment(Payment::try_new(...)).
contracts/feature-tests/use-module/tests/staking_module_whitebox_test.rs Replaces TestEsdtTransfer with Payment::try_new(...).unwrap().
contracts/feature-tests/use-module/tests/gov_module_whitebox_test.rs Replaces TestEsdtTransfer with Payment::try_new(...).unwrap().
contracts/feature-tests/payable-features/tests/payable_blackbox_test.rs Adds blackbox coverage for composing 0/1/2 payments; keeps legacy path test.
contracts/feature-tests/composability/vault/src/vault.rs Makes retrieve_funds amount non-zero (NonZeroBigUint) and uses Payment for ESDT transfers.
contracts/feature-tests/composability/transfer-role-features/tests/transfer_role_whitebox_test.rs Replaces TestEsdtTransfer with Payment::try_new(...).unwrap().
contracts/feature-tests/composability/tests/forwarder_whitebox_test.rs Migrates .esdt(...)/TestEsdtTransfer usages to .payment(Payment::try_new(...)).
contracts/feature-tests/composability/tests/forwarder_whitebox_legacy_test.rs Same migration as above for legacy forwarder tests.
contracts/feature-tests/composability/local-esdt-and-nft/src/lib.rs Migrates ESDT async send helper to Payment::new(...) + .payment(...).
contracts/feature-tests/composability/forwarder-legacy/src/fwd_call_sync_legacy.rs Updates forwarder legacy call signature to require NonZeroBigUint amount.
contracts/feature-tests/composability/forwarder-legacy/src/fwd_call_async_legacy.rs Updates forwarder legacy call signature to require NonZeroBigUint amount.
contracts/feature-tests/basic-features/interact/tests/bf_interact_cs_test.rs Simplifies imports and migrates ESDT send to Payment::try_new(...).unwrap().
Cargo.lock Removes chrono entry (lockfile update).

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

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 41 out of 42 changed files in this pull request and generated 6 comments.


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

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 53 out of 54 changed files in this pull request and generated 3 comments.


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

@andrei-marinica andrei-marinica merged commit 1a9060d into rc/v0.65 Feb 11, 2026
23 checks passed
@andrei-marinica andrei-marinica deleted the test-payment branch February 11, 2026 20:02
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.

1 participant