Skip to content

Commit 3a28fba

Browse files
committed
fix: allocations and withdraw executions can't be parallel
1 parent 2539a27 commit 3a28fba

File tree

3 files changed

+49
-38
lines changed

3 files changed

+49
-38
lines changed

common/src/vault.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,16 @@ pub const AFTER_SEND_TO_USER_GAS: Gas = Gas::from_tgas(5);
145145

146146
// Add a 20% buffer to a gas estimate
147147
pub const fn buffer(size: usize) -> Gas {
148-
Gas::from_tgas(size as u64 * 2 / 5)
148+
// 20% buffer => multiply by 6/5 (≈1.2x)
149+
Gas::from_tgas((size as u64 * 6) / 5)
149150
}
150151

151152
// NOTE: these are taken after running the contract with the gas report
152153
pub const SUPPLY_GAS: Gas = buffer(8);
153154
pub const ALLOCATE_GAS: Gas = buffer(21);
154155
pub const WITHDRAW_GAS: Gas = buffer(4);
155156
pub const EXECUTE_WITHDRAW_GAS: Gas = buffer(9);
157+
pub const SUBMIT_CAP_GAS: Gas = buffer(4);
156158

157159
pub fn require_at_least(needed: Gas) {
158160
let gas = env::prepaid_gas();

contract/vault/examples/gas_report.rs

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,61 +23,72 @@ async fn main() {
2323
let weights = vec![(c.market.contract().id().clone(), U128(1))];
2424
let user1_amount = max / ITERATIONS as u128;
2525

26-
let futures = (0..ITERATIONS).map(|_| async {
27-
let supply_gas = vault
26+
// Run supplies concurrently.
27+
let supply_futures = (0..ITERATIONS).map(|_| async {
28+
vault
2829
.supply(&user1, user1_amount)
2930
.await
3031
.total_gas_burnt
31-
.as_gas() as f64;
32+
.as_gas() as f64
33+
});
34+
let supply_results = futures::future::join_all(supply_futures).await;
35+
36+
let mut supply_gas_average = 0f64;
37+
for s in supply_results {
38+
supply_gas_average += s / ITERATIONS as f64;
39+
}
3240

41+
let mut allocation_gas_average = 0f64;
42+
for _ in 0..ITERATIONS {
3343
let allocation_gas = vault
3444
.allocate(&vault_curator, weights.clone(), Some(U128(user1_amount)))
3545
.await
3646
.total_gas_burnt
3747
.as_gas() as f64;
38-
39-
(supply_gas, allocation_gas)
40-
});
41-
let results = futures::future::join_all(futures).await.into_iter();
42-
43-
let mut supply_gas_average = 0f64;
44-
let mut allocation_gas_average = 0f64;
45-
// Aggregate and compute averages.
46-
for (s, a) in results {
47-
supply_gas_average += s / ITERATIONS as f64;
48-
allocation_gas_average += a / ITERATIONS as f64;
48+
allocation_gas_average += allocation_gas / ITERATIONS as f64;
4949
}
5050

5151
// Supply to vault
5252
let user2_amount = g();
53+
vault.supply(&user2, user2_amount).await;
54+
5355
let user3_amount = g();
54-
tokio::join!(
55-
vault.supply(&user2, user2_amount),
56-
vault.supply(&user3, user3_amount)
57-
);
5856

59-
// Create all futures first so they can be awaited concurrently.
60-
let futures = (0..ITERATIONS).map(|_| async {
61-
let withdraw_gas = vault
57+
// Submitting a smaller gas limit will not require a timelock
58+
let submit_cap_gas = vault
59+
.submit_cap(
60+
&vault_curator,
61+
c.market.contract().id().clone(),
62+
U128(user3_amount),
63+
)
64+
.await
65+
.total_gas_burnt
66+
.as_gas() as f64;
67+
68+
vault.supply(&user3, user3_amount).await;
69+
70+
let withdraw_futures = (0..ITERATIONS).map(|_| async {
71+
vault
6272
.withdraw(&user2, U128(1), None)
6373
.await
6474
.total_gas_burnt
65-
.as_gas() as f64;
75+
.as_gas() as f64
76+
});
77+
let withdraw_results = futures::future::join_all(withdraw_futures).await;
78+
79+
let mut withdraw_gas_average = 0f64;
80+
for w in withdraw_results {
81+
withdraw_gas_average += w / ITERATIONS as f64;
82+
}
83+
84+
let mut execute_withdraw_gas_average = 0f64;
85+
for _ in 0..ITERATIONS {
6686
let execute_gas = vault
6787
.execute_next_withdrawal(&vault_curator)
6888
.await
6989
.total_gas_burnt
7090
.as_gas() as f64;
71-
(withdraw_gas, execute_gas)
72-
});
73-
74-
let results = futures::future::join_all(futures).await;
75-
76-
let mut withdraw_gas_average = 0f64;
77-
let mut execute_withdraw_gas_average = 0f64;
78-
for (w, e) in results {
79-
withdraw_gas_average += w / ITERATIONS as f64;
80-
execute_withdraw_gas_average += e / ITERATIONS as f64;
91+
execute_withdraw_gas_average += execute_gas / ITERATIONS as f64;
8192
}
8293

8394
println!("## Gas Report");
@@ -96,6 +107,7 @@ async fn main() {
96107
"execute withdraw",
97108
Gas::from_gas(execute_withdraw_gas_average as u64),
98109
),
110+
("submit_cap", Gas::from_gas(submit_cap_gas as u64)),
99111
];
100112
for (action_label, gas) in list {
101113
println!("| `{action_label}` | {gas} |");

contract/vault/src/lib.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -741,13 +741,10 @@ impl Contract {
741741
#[payable]
742742
pub fn redeem(&mut self, shares: U128, receiver: AccountId) -> PromiseOrValue<()> {
743743
let shares = shares.0;
744-
745744
let assets = self.convert_to_assets(U128(shares)).0;
746-
747745
let sender = env::predecessor_account_id();
748746

749-
// Require storage deposit for the pending withdrawal entry
750-
let req_yocto = require_attached_for_pending_withdrawal();
747+
require_attached_for_pending_withdrawal();
751748

752749
// Move shares into escrow
753750
#[allow(clippy::expect_used, reason = "No side effects")]
@@ -1171,7 +1168,7 @@ impl Contract {
11711168
fn ensure_idle(&self) {
11721169
// Invariant: Only one op in flight; ensure_idle() guards all mutating ops.
11731170
if !matches!(self.op_state, OpState::Idle) {
1174-
env::panic_str("busy");
1171+
env::panic_str("Invariant: Only one op in flight");
11751172
}
11761173
}
11771174

0 commit comments

Comments
 (0)