@@ -467,6 +467,65 @@ TEST(TWTHORChainSwap, NegativeOverflowToAmountLimit) {
467467 EXPECT_EQ (outputProto.error ().code (), Proto::ErrorCode::Error_general);
468468 EXPECT_FALSE (outputProto.has_bitcoin ());
469469}
470+
471+ // Regression: a from_amount with a leading zero used to pass the digit-only pre-check and then
472+ // be misparsed as octal by uint256_t's string constructor (e.g. "0500" -> 320), silently
473+ // building a swap for the wrong amount. It must now be rejected outright.
474+ TEST (TWTHORChainSwap, NegativeLeadingZeroFromAmount) {
475+ Proto::SwapInput input;
476+ Proto::Asset fromAsset;
477+ fromAsset.set_chain (Proto::BTC );
478+ *input.mutable_from_asset () = fromAsset;
479+ input.set_from_address (Address1Btc);
480+ Proto::Asset toAsset;
481+ toAsset.set_chain (Proto::ETH );
482+ toAsset.set_symbol (" ETH" );
483+ *input.mutable_to_asset () = toAsset;
484+ input.set_to_address (Address1Eth);
485+ input.set_vault_address (VaultBtc);
486+ input.set_from_amount (" 0500" );
487+ input.set_to_amount_limit (" 140000000000000000" );
488+
489+ const auto inputData_ = input.SerializeAsString ();
490+ const auto inputTWData_ = WRAPD (TWDataCreateWithBytes ((const uint8_t *)inputData_.data (), inputData_.size ()));
491+
492+ const auto outputTWData_ = WRAPD (TWTHORChainSwapBuildSwap (inputTWData_.get ()));
493+ const auto outputData = data (TWDataBytes (outputTWData_.get ()), TWDataSize (outputTWData_.get ()));
494+ Proto::SwapOutput outputProto;
495+ ASSERT_TRUE (outputProto.ParseFromArray (outputData.data (), static_cast <int >(outputData.size ())));
496+ EXPECT_EQ (outputProto.error ().code (), Proto::ErrorCode::Error_general);
497+ EXPECT_FALSE (outputProto.has_bitcoin ());
498+ }
499+
500+ // Regression: an all-digit from_amount larger than 2^256-1 used to pass the digit-only
501+ // pre-check and then silently wrap around inside uint256_t's fixed-width arithmetic, building
502+ // a swap for an arbitrary, unrelated amount. It must now be rejected outright.
503+ TEST (TWTHORChainSwap, NegativeOverflowFromAmount) {
504+ Proto::SwapInput input;
505+ Proto::Asset fromAsset;
506+ fromAsset.set_chain (Proto::BTC );
507+ *input.mutable_from_asset () = fromAsset;
508+ input.set_from_address (Address1Btc);
509+ Proto::Asset toAsset;
510+ toAsset.set_chain (Proto::ETH );
511+ toAsset.set_symbol (" ETH" );
512+ *input.mutable_to_asset () = toAsset;
513+ input.set_to_address (Address1Eth);
514+ input.set_vault_address (VaultBtc);
515+ // 2^256 == 115792089237316195423570985008687907853269984665640564039457584007913129639936
516+ input.set_from_amount (" 115792089237316195423570985008687907853269984665640564039457584007913129639936" );
517+ input.set_to_amount_limit (" 140000000000000000" );
518+
519+ const auto inputData_ = input.SerializeAsString ();
520+ const auto inputTWData_ = WRAPD (TWDataCreateWithBytes ((const uint8_t *)inputData_.data (), inputData_.size ()));
521+
522+ const auto outputTWData_ = WRAPD (TWTHORChainSwapBuildSwap (inputTWData_.get ()));
523+ const auto outputData = data (TWDataBytes (outputTWData_.get ()), TWDataSize (outputTWData_.get ()));
524+ Proto::SwapOutput outputProto;
525+ ASSERT_TRUE (outputProto.ParseFromArray (outputData.data (), static_cast <int >(outputData.size ())));
526+ EXPECT_EQ (outputProto.error ().code (), Proto::ErrorCode::Error_general);
527+ EXPECT_FALSE (outputProto.has_bitcoin ());
528+ }
470529// clang-format on
471530
472531} // namespace TW::ThorChainSwap::tests
0 commit comments