Contract fails to deliver promised returns, but doesn't lose value
Description
Brief/Intro
in require_at_least_min_net_debt, if it did not pass the check, it will emit a error message "Borrow Operations: net debt must be greater than 0", it did not consider the value of MIN_NET_DEBT in the error message.
Vulnerability Details
in contracts/borrow-operations-contract/src/main.sw
fn require_at_least_min_net_debt(_net_debt: u64) {
require(
_net_debt >= MIN_NET_DEBT,
"Borrow Operations: net debt must be greater than 0", // @audit: error message
);
}
but MIN_NET_DEBT is 500 usdf defined in fluid_math.sw
// min debt is 500 USDF for staging
pub const MIN_NET_DEBT: u64 = 500_000_000_000;
It is suggested to emit correct error message like :
fn require_at_least_min_net_debt(_net_debt: u64) {
require(
_net_debt >= MIN_NET_DEBT,
"Borrow Operations: net debt must be greater than 500", // @audit: error message
);
}
It is even better to use format string to fill MIN_NET_DEBT into the error message, but I could not find out how to do it
Impact Details
Deliver wrong message when the checks fails, makes it hard to debug and confuse the users.
References
None
Proof of Concept
Proof of Concept
change the function fails_open_trove_under_min_usdf_required in contracts/borrow-operations-contract/tests/failurelike:
#[tokio::test]
async fn fails_open_trove_under_min_usdf_required() {
let (contracts, admin, _) = setup_protocol(2, false, false).await;
token_abi::mint_to_id(
&contracts.asset_contracts[0].asset,
5_000 * PRECISION,
Identity::Address(admin.address().into()),
)
.await;
let coll_amount = 1_200 * PRECISION;
let debt_amount = 400 * PRECISION;
// 100 USDF < 500 USDF
oracle_abi::set_debug_timestamp(&contracts.asset_contracts[0].oracle, PYTH_TIMESTAMP).await;
pyth_oracle_abi::update_price_feeds(
&contracts.asset_contracts[0].mock_pyth_oracle,
pyth_price_feed(1),
)
.await;
let res = borrow_operations_abi::open_trove(
&contracts.borrow_operations,
&contracts.asset_contracts[0].oracle,
&contracts.asset_contracts[0].mock_pyth_oracle,
&contracts.asset_contracts[0].mock_redstone_oracle,
&contracts.asset_contracts[0].asset,
&contracts.usdf,
&contracts.fpt_staking,
&contracts.sorted_troves,
&contracts.asset_contracts[0].trove_manager,
&contracts.active_pool,
coll_amount,
debt_amount,
Identity::Address(Address::zeroed()),
Identity::Address(Address::zeroed()),
)
.await;
if let Err(error) = res {
assert!(
error
.to_string()
.contains("net debt must be greater than 500"),
"Incorrect error message: {}",
error
);
}
}
and then run cargo test fails_open_trove_under_min_usdf_required -- --nocapture, the output is like :
running 1 test
Deploying core contracts...
Initializing core contracts...
thread 'fails_open_trove_under_min_usdf_required' panicked at contracts/borrow-operations-contract/tests/failure.rs:279:9:
Incorrect error message: transaction reverted: AsciiString { data: "Borrow Operations: net debt must be greater than 0" }, receipts: [Call { id: 0000000000000000000000000000000000000000000000000000000000000000, to: 25fbb8fb8ceb5e847bfc1c6f0f215b9b2aab5ce6462cce8671b46adaf99cd199, amount: 1200000000000, asset_id: 9a464b41647f372808cb8f20f8f7aa8eef27acdfd9e6aaa166b1a1ae8b35d7f4, gas: 50472, param1: 10480, param2: 10498, pc: 15624, is: 15624 }, Call { id: 25fbb8fb8ceb5e847bfc1c6f0f215b9b2aab5ce6462cce8671b46adaf99cd199, to: ab8d58390cc175702ab3059fbf77e43d48a36c17d9b2fa530968806a3a31fbd8, amount: 0, asset_id: 0000000000000000000000000000000000000000000000000000000000000000, gas: 34972, param1: 67104704, param2: 67103680, pc: 131448, is: 131448 }, Call { id: ab8d58390cc175702ab3059fbf77e43d48a36c17d9b2fa530968806a3a31fbd8, to: c672c33f1411c7c77d6d4f169b89c9e18060c79a8eb434c0d160cd9b4d1b74d8, amount: 0, asset_id: 0000000000000000000000000000000000000000000000000000000000000000, gas: 30993, param1: 67101056, param2: 67100032, pc: 183393, is: 183393 }, ReturnData { id: c672c33f1411c7c77d6d4f169b89c9e18060c79a8eb434c0d160cd9b4d1b74d8, ptr: 67097472, len: 28, digest: a9dd65fcff83b6bccf652da94d61edd8632ff002061afc77e498266005f6be0c, pc: 185429, is: 183393, data: Some(00000000000000000000000900...) }, ReturnData { id: ab8d58390cc175702ab3059fbf77e43d48a36c17d9b2fa530968806a3a31fbd8, ptr: 67095680, len: 8, digest: 59f603c39018dc65fbf3007d91985355b0e27df2993aab3c4a9e4b5ea36c5996, pc: 148732, is: 131448, data: Some(000000003b9aca00) }, Call { id: 25fbb8fb8ceb5e847bfc1c6f0f215b9b2aab5ce6462cce8671b46adaf99cd199, to: 82d6fe3974112bf9994034b4a425998e635a89521fc6747e1b38ba016b717cb8, amount: 0, asset_id: 0000000000000000000000000000000000000000000000000000000000000000, gas: 25254, param1: 67094656, param2: 67093632, pc: 131600, is: 131600 }, ReturnData { id: 82d6fe3974112bf9994034b4a425998e635a89521fc6747e1b38ba016b717cb8, ptr: 67090171, len: 8, digest: af5570f5a1810b7af78caf4bc70a660f0df51e42baf91d4de5b2328de0e83dfc, pc: 157732, is: 131600, data: Some(0000000000000000) }, Call { id: 25fbb8fb8ceb5e847bfc1c6f0f215b9b2aab5ce6462cce8671b46adaf99cd199, to: 371f0b98ae91d7e9198dab7cd5714e22645813a787f11d0c80375849619d1f06, amount: 0, asset_id: 0000000000000000000000000000000000000000000000000000000000000000, gas: 15206, param1: 67089147, param2: 67088123, pc: 132624, is: 132624 }, ReturnData { id: 371f0b98ae91d7e9198dab7cd5714e22645813a787f11d0c80375849619d1f06, ptr: 0, len: 0, digest: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855, pc: 151880, is: 132624, data: Some() }, Call { id: 25fbb8fb8ceb5e847bfc1c6f0f215b9b2aab5ce6462cce8671b46adaf99cd199, to: 183ba321f9edc4afa185a7ca9001eeba214a52753ea2d1392f1593e1776492d2, amount: 0, asset_id: 0000000000000000000000000000000000000000000000000000000000000000, gas: 8880, param1: 67084987, param2: 67083963, pc: 133912, is: 133912 }, Mint { sub_id: 0000000000000000000000000000000000000000000000000000000000000000, contract_id: 183ba321f9edc4afa185a7ca9001eeba214a52753ea2d1392f1593e1776492d2, val: 2000000000, pc: 175720, is: 133912 }, Transfer { id: 183ba321f9edc4afa185a7ca9001eeba214a52753ea2d1392f1593e1776492d2, to: 371f0b98ae91d7e9198dab7cd5714e22645813a787f11d0c80375849619d1f06, amount: 2000000000, asset_id: 0a9bf6faca63498a8e53497a1d9cdb4e7a772d1e65d93999a0d1550a1e47db2c, pc: 175992, is: 133912 }, LogData { id: 183ba321f9edc4afa185a7ca9001eeba214a52753ea2d1392f1593e1776492d2, ra: 0, rb: 17462098202904023478, ptr: 67080315, len: 80, digest: 75fba1c44ab4fce453cea9982bd94199a56df408c4d7cbf498c04ca3a828ee7a, pc: 171716, is: 133912, data: Some(0a9bf6faca63498a8e53497a1d...) }, ReturnData { id: 183ba321f9edc4afa185a7ca9001eeba214a52753ea2d1392f1593e1776492d2, ptr: 0, len: 0, digest: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855, pc: 154544, is: 133912, data: Some() }, LogData { id: 25fbb8fb8ceb5e847bfc1c6f0f215b9b2aab5ce6462cce8671b46adaf99cd199, ra: 0, rb: 10098701174489624218, ptr: 67079291, len: 58, digest: c1130903e915571f01a555bdde08cda22ec45f1c491f0e4fa93c8dcace05a5d1, pc: 46492, is: 15624, data: Some(0000000000000032426f72726f...) }, Revert { id: 25fbb8fb8ceb5e847bfc1c6f0f215b9b2aab5ce6462cce8671b46adaf99cd199, ra: 18446744073709486080, pc: 46500, is: 15624 }, ScriptResult { result: Revert, gas_used: 48315 }]
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
test fails_open_trove_under_min_usdf_required ... FAILED
failures:
failures:
fails_open_trove_under_min_usdf_required