Skip to content

Conversation

@andrei-marinica
Copy link
Contributor

No description provided.

@github-actions
Copy link

Contract comparison - from dcf00bb to 602f574

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 14060 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 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 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 false None
vault-upgrade.wasm 708 false None
forwarder.wasm 48793 false without message
proxy-test-second.wasm 2327 false without message
local-esdt-and-nft.wasm 12254 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 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

⚠️ Could not download the report for the base branch. Displaying only the report for the current branch. ⚠️

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

Refactors the order-book example contract to use the framework’s FungiblePayment / NonZeroBigUint types and introduces proportion/into_proportion helpers for big number types, with added scenario tests for these new helpers.

Changes:

  • Added proportion / into_proportion APIs on BigInt, BigUint, and NonZeroBigUint.
  • Refactored the order-book example to use FungiblePayment and NonZeroBigUint amounts.
  • Added/expanded scenario tests for proportion behavior (including NonZeroBigUint panic behavior).

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
framework/scenario/tests/non_zero_big_uint_test.rs Adds tests for NonZeroBigUint proportion behavior and panic on zero result.
framework/scenario/tests/big_uint_test.rs Adds BigUint proportion tests alongside existing ln tests.
framework/scenario/tests/big_int_test.rs Adds BigInt proportion tests.
framework/base/src/types/managed/wrapped/num/non_zero_big_uint_operators.rs Exposes an internal helper (wrap_big_int_assert_gt_zero) for use by new APIs.
framework/base/src/types/managed/wrapped/num/non_zero_big_uint.rs Adds NonZeroBigUint::{proportion, into_proportion} with rustdoc.
framework/base/src/types/managed/wrapped/num/big_uint.rs Adds BigUint::{proportion, into_proportion} and new_unchecked, with rustdoc.
framework/base/src/types/managed/basic/big_int.rs Makes overwrite_i64 mutable and adds BigInt::{proportion, into_proportion}.
contracts/feature-tests/basic-features/src/big_num_methods.rs Updates endpoint to pass BigInt as mutable due to signature change.
contracts/examples/order-book/pair/src/validation.rs Switches to FungiblePayment/NonZeroBigUint and updates fee/payment validation logic.
contracts/examples/order-book/pair/src/orders.rs Refactors order processing to use new payment/amount types and new proportion helpers.
contracts/examples/order-book/pair/src/common.rs Replaces custom payment struct with FungiblePayment and makes order amounts non-zero.

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

/// let result = amount.into_proportion(5u32, 100u32); // 5/100 of 1000 = 50
/// ```
pub fn into_proportion(self, part: u64, total: u64) -> Self {
let result_big_int = self.value.into_proportion(part as i64, total as i64);
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

NonZeroBigUint::into_proportion() casts part/total from u64 to i64 using as, which wraps for values > i64::MAX and can turn the computation negative. That would either violate the non-zero/positive invariant or trigger an unexpected panic in wrap_big_int_assert_gt_zero. Use a checked conversion (e.g. cast_to_i64) and explicitly guard against total == 0 to avoid division-by-zero traps.

Suggested change
let result_big_int = self.value.into_proportion(part as i64, total as i64);
if total == 0 {
quick_signal_error(err_msg!(
"NonZeroBigUint::into_proportion: total must be non-zero"
));
}
let part_i64 = i64::try_from(part).unwrap_or_else(|_| {
quick_signal_error(err_msg!(
"NonZeroBigUint::into_proportion: part too large to fit in i64"
))
});
let total_i64 = i64::try_from(total).unwrap_or_else(|_| {
quick_signal_error(err_msg!(
"NonZeroBigUint::into_proportion: total too large to fit in i64"
))
});
let result_big_int = self.value.into_proportion(part_i64, total_i64);

Copilot uses AI. Check for mistakes.
let amount = &order.input_amount - &penalty_amount;
let penalty_amount = order
.input_amount
.proportion(penalty_percent, PERCENT_BASE_POINTS);
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

penalty_percent can be 0 (e.g. cancelling shortly after creation), but order.input_amount.proportion(...) returns NonZeroBigUint and will panic on a 0 result. Penalties should be allowed to be zero here; compute penalty_amount as BigUint from order.input_amount.as_big_uint() and then subtract without forcing non-zero.

Suggested change
.proportion(penalty_percent, PERCENT_BASE_POINTS);
.as_big_uint()
.clone()
* penalty_percent
/ PERCENT_BASE_POINTS;

Copilot uses AI. Check for mistakes.
Comment on lines +315 to +316
/// let amount = BigUint::from(1000u32);
/// let result = amount.into_proportion(5u32, 100u32); // 5/100 of 1000 = 50
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

The new rustdoc example uses BigUint::from(...) without imports or a concrete ManagedTypeApi type parameter. Unlike other docs in this crate (e.g. framework/base/src/types/managed/wrapped/token/egld_or_esdt_token_identifier.rs:20-25), this doctest is likely to fail. Consider adding hidden prelude lines (# use ...) plus a generic M: ManagedTypeApi wrapper, or mark the snippet no_run if it can’t be executed in a doctest environment.

Suggested change
/// let amount = BigUint::from(1000u32);
/// let result = amount.into_proportion(5u32, 100u32); // 5/100 of 1000 = 50
/// # use crate::api::ManagedTypeApi;
/// # use crate::types::BigUint;
/// fn example<M: ManagedTypeApi>() {
/// let amount = BigUint::<M>::from(1000u32);
/// let result = amount.into_proportion(5u64, 100u64); // 5/100 of 1000 = 50
/// }

Copilot uses AI. Check for mistakes.
Comment on lines +189 to +190
/// let amount = NonZeroBigUint::new_or_panic(BigUint::from(1000u32));
/// let result = amount.into_proportion(5u32, 100u32); // 5/100 of 1000 = 50
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

The new rustdoc examples use NonZeroBigUint::new_or_panic(...)/BigUint::from(...) without imports or a concrete ManagedTypeApi type parameter. This is inconsistent with other doctestable examples in this crate and is likely to fail when running doctests. Add the necessary hidden imports and a generic M: ManagedTypeApi wrapper, or mark the snippet no_run.

Suggested change
/// let amount = NonZeroBigUint::new_or_panic(BigUint::from(1000u32));
/// let result = amount.into_proportion(5u32, 100u32); // 5/100 of 1000 = 50
/// # use multiversx_sc::types::{BigUint, NonZeroBigUint};
/// # use multiversx_sc::api::ManagedTypeApi;
/// # fn example<M: ManagedTypeApi>() {
/// let amount = NonZeroBigUint::<M>::new_or_panic(BigUint::<M>::from(1000u32));
/// let result = amount.into_proportion(5u32, 100u32); // 5/100 of 1000 = 50
/// # let _ = result;
/// # }

Copilot uses AI. Check for mistakes.
Comment on lines +319 to +323
// mathematically, the result of this operation cannot be negative, so it is safe to skip sign check
unsafe {
Self::new_unchecked(
self.into_big_int()
.into_proportion(part as i64, total as i64),
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

BigUint::into_proportion() casts part/total from u64 to i64 with as. For values > i64::MAX, this wraps to a negative number, which can yield a negative BigInt and then unsafe new_unchecked breaks the BigUint invariant (>= 0). Use the existing checked conversion (cast_to_i64) or change the API to accept i64/u32, and reject total == 0 explicitly to avoid VM division-by-zero traps.

Suggested change
// mathematically, the result of this operation cannot be negative, so it is safe to skip sign check
unsafe {
Self::new_unchecked(
self.into_big_int()
.into_proportion(part as i64, total as i64),
// Explicitly reject zero denominator to avoid division-by-zero in the underlying VM.
assert!(total != 0, "BigUint::into_proportion: total must be non-zero");
// Use checked conversion from u64 to i64 to avoid wrapping into negative values.
let part_i64: i64 = part
.try_into()
.expect("BigUint::into_proportion: part exceeds i64::MAX");
let total_i64: i64 = total
.try_into()
.expect("BigUint::into_proportion: total exceeds i64::MAX");
// Mathematically, with non-negative inputs and non-zero denominator, result cannot be negative,
// so it is safe to skip sign check after ensuring conversions did not wrap.
unsafe {
Self::new_unchecked(
self.into_big_int()
.into_proportion(part_i64, total_i64),

Copilot uses AI. Check for mistakes.
Comment on lines +384 to +389
pub fn into_proportion(mut self, part: i64, total: i64) -> Self {
let mut temp = BigInt::from(part);
self *= &temp;
temp.overwrite_i64(total);
self /= &temp;
self
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

BigInt::into_proportion() divides by total without guarding total == 0. Since this is a safe public API, passing 0 will likely trap in the VM (bi_t_div). Consider signaling an explicit error (or returning Result) when total == 0, and document the rounding/truncation semantics.

Copilot uses AI. Check for mistakes.
FeeConfigEnum::Fixed => {
require!(
params.fee_config.fixed_fee < params.amount,
params.fee_config.fixed_fee < *params.amount.as_big_uint(),
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

This comparison attempts to dereference params.amount.as_big_uint() (*...), which tries to move a BigUint out of a shared reference and will not compile. Compare by reference or clone the RHS (e.g. params.fee_config.fixed_fee < params.amount.as_big_uint().clone()).

Suggested change
params.fee_config.fixed_fee < *params.amount.as_big_uint(),
&params.fee_config.fixed_fee < params.amount.as_big_uint(),

Copilot uses AI. Check for mistakes.
Comment on lines 78 to 81
);

OrderBookFungiblePayment {
token_id: payment.token_identifier.clone(),
amount: payment.amount.as_big_uint().clone(),
}
FungiblePayment::new(payment.token_identifier.clone(), payment.amount.clone())
}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

FungiblePayment::new expects a NonZeroBigUint amount, but call_value().single().amount is a BigUint. This won’t compile and also drops the previous non-zero validation. Convert with NonZeroBigUint::new(...)/new_or_panic(...) and add an explicit require!(payment.amount != 0, ...) (or equivalent) before constructing the FungiblePayment.

Copilot uses AI. Check for mistakes.
Comment on lines 90 to 93
);

OrderBookFungiblePayment {
token_id: payment.token_identifier.clone(),
amount: payment.amount.as_big_uint().clone(),
}
FungiblePayment::new(payment.token_identifier.clone(), payment.amount.clone())
}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

FungiblePayment::new expects a NonZeroBigUint amount, but call_value().single().amount is a BigUint. This won’t compile and also allows zero-amount payments. Validate payment.amount != 0 and convert to NonZeroBigUint before constructing the FungiblePayment.

Copilot uses AI. Check for mistakes.
Comment on lines +156 to 161
let penalty_amount = order
.input_amount
.proportion(penalty_percent, PERCENT_BASE_POINTS);

let creator_amount = &order.input_amount - &penalty_amount;

Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

order.input_amount is a NonZeroBigUint, so calling proportion(...) returns a NonZeroBigUint and will panic if the computed penalty truncates to 0 (e.g. small input_amount) or if penalty_percent == 0. Penalties can legitimately be 0 here, so compute the penalty as BigUint (e.g. order.input_amount.as_big_uint().proportion(...)) and only convert to NonZeroBigUint when you know the result must be non-zero.

Suggested change
let penalty_amount = order
.input_amount
.proportion(penalty_percent, PERCENT_BASE_POINTS);
let creator_amount = &order.input_amount - &penalty_amount;
// Compute penalty on BigUint to allow zero penalties without panicking.
let input_amount_big = order.input_amount.as_big_uint();
let penalty_amount = input_amount_big.proportion(penalty_percent, PERCENT_BASE_POINTS);
let creator_amount = &input_amount_big - &penalty_amount;

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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants