Skip to content

Commit dd83754

Browse files
committed
feat(soroban): return typed execute receipts
1 parent f6c17f2 commit dd83754

25 files changed

Lines changed: 1183 additions & 372 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ redundant_closure_for_method_calls = "allow"
138138
module_name_repetitions = "allow"
139139
missing_errors_doc = "allow"
140140
missing_panics_doc = "allow"
141-
unwrap_used = "allow"
141+
unwrap_used = "deny"
142142
expect_used = "warn"
143143
large_digit_groups = "warn"
144144
wildcard_imports = "allow"

contract/proxy-4626-soroban/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ templar-curator-primitives = { path = "../vault/curator-primitives", features =
2222

2323
[dev-dependencies]
2424
soroban-sdk = { version = "25.0.1", features = ["testutils", "alloc"] }
25+
templar-soroban-governance = { path = "../vault/soroban/governance" }
2526
templar-soroban-runtime = { path = "../vault/soroban", features = ["testutils"] }

contract/proxy-4626-soroban/src/contract.rs

Lines changed: 99 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use soroban_sdk::{
2020
contract, contractimpl, symbol_short, Address, Bytes, Env, IntoVal, InvokeError, Symbol,
2121
};
2222
use templar_soroban_shared_types::{
23-
ProxyPreviewFields, ProxyViewFields, ProxyViewResponse, VaultCommand as WireVaultCommand,
24-
VaultCommandResult as WireVaultCommandResult,
23+
strkey::validate_address_strkey, DepositReceipt, ExecuteWithdrawReceipt, ProxyPreviewFields,
24+
ProxyViewFields, ProxyViewResponse, RequestWithdrawReceipt, VaultCommand as WireVaultCommand,
2525
};
2626

2727
use crate::error::ContractError;
@@ -146,7 +146,7 @@ impl Soroban4626ProxyContract {
146146
let preview = call_proxy_view(&env, &operator, 0, shares)?;
147147
let assets = preview.preview_mint_assets;
148148
require_non_negative(assets)?;
149-
let minted_shares = expect_i128_result(invoke_vault_execute(
149+
let receipt = decode_deposit_receipt(invoke_vault_execute(
150150
&env,
151151
VaultCommand::DepositWithMin {
152152
owner: operator.clone(),
@@ -155,7 +155,7 @@ impl Soroban4626ProxyContract {
155155
min_shares_out: shares,
156156
},
157157
)?)?;
158-
emit_deposit_event(&env, &operator, &receiver, assets, minted_shares);
158+
emit_deposit_event(&env, &operator, &receiver, assets, receipt.shares_out);
159159
Ok(assets)
160160
}
161161

@@ -182,7 +182,7 @@ impl Soroban4626ProxyContract {
182182
let preview = call_proxy_view(&env, &owner, assets, 0)?;
183183
let shares = preview.preview_withdraw_shares;
184184
require_non_negative(shares)?;
185-
let request_id = expect_u64_result(invoke_vault_execute(
185+
let receipt = decode_request_withdraw_receipt(invoke_vault_execute(
186186
&env,
187187
VaultCommand::RequestWithdraw {
188188
owner: owner.clone(),
@@ -191,8 +191,15 @@ impl Soroban4626ProxyContract {
191191
min_assets_out: assets,
192192
},
193193
)?)?;
194-
emit_redeem_request_event(&env, &receiver, &owner, request_id, &operator, shares);
195-
Ok(request_id)
194+
emit_redeem_request_event(
195+
&env,
196+
&receiver,
197+
&owner,
198+
receipt.request_id,
199+
&operator,
200+
receipt.shares_escrowed,
201+
);
202+
Ok(receipt.request_id)
196203
}
197204

198205
/// Request an asynchronous redemption by share amount.
@@ -218,7 +225,7 @@ impl Soroban4626ProxyContract {
218225
let preview = call_proxy_view(&env, &owner, 0, shares)?;
219226
let assets = preview.convert_to_assets;
220227
require_non_negative(assets)?;
221-
let request_id = expect_u64_result(invoke_vault_execute(
228+
let receipt = decode_request_withdraw_receipt(invoke_vault_execute(
222229
&env,
223230
VaultCommand::RequestWithdraw {
224231
owner: owner.clone(),
@@ -227,8 +234,15 @@ impl Soroban4626ProxyContract {
227234
min_assets_out: assets,
228235
},
229236
)?)?;
230-
emit_redeem_request_event(&env, &receiver, &owner, request_id, &operator, shares);
231-
Ok(request_id)
237+
emit_redeem_request_event(
238+
&env,
239+
&receiver,
240+
&owner,
241+
receipt.request_id,
242+
&operator,
243+
receipt.shares_escrowed,
244+
);
245+
Ok(receipt.request_id)
232246
}
233247

234248
/// Lower-level asynchronous redemption request with explicit slippage.
@@ -247,7 +261,7 @@ impl Soroban4626ProxyContract {
247261
require_non_negative(shares)?;
248262
require_non_negative(min_assets_out)?;
249263
owner.require_auth();
250-
let request_id = expect_u64_result(invoke_vault_execute(
264+
let receipt = decode_request_withdraw_receipt(invoke_vault_execute(
251265
&env,
252266
VaultCommand::RequestWithdraw {
253267
owner: owner.clone(),
@@ -256,8 +270,15 @@ impl Soroban4626ProxyContract {
256270
min_assets_out,
257271
},
258272
)?)?;
259-
emit_redeem_request_event(&env, &receiver, &owner, request_id, &owner, shares);
260-
Ok(request_id)
273+
emit_redeem_request_event(
274+
&env,
275+
&receiver,
276+
&owner,
277+
receipt.request_id,
278+
&owner,
279+
receipt.shares_escrowed,
280+
);
281+
Ok(receipt.request_id)
261282
}
262283

263284
/// Execute the next claimable queued withdrawal.
@@ -267,10 +288,34 @@ impl Soroban4626ProxyContract {
267288
/// policy; it does not select a withdrawal request by `request_id`.
268289
pub fn execute_withdraw(env: Env, operator: Address) -> Result<(), ContractError> {
269290
operator.require_auth();
270-
expect_unit_result(invoke_vault_execute(
291+
let receipt = decode_execute_withdraw_receipt(invoke_vault_execute(
271292
&env,
272-
VaultCommand::ExecuteWithdraw { caller: operator },
273-
)?)
293+
VaultCommand::ExecuteWithdraw {
294+
caller: operator.clone(),
295+
},
296+
)?)?;
297+
if let ExecuteWithdrawReceipt::Completed {
298+
request_id,
299+
owner,
300+
receiver,
301+
assets_out,
302+
shares_burned,
303+
..
304+
} = receipt
305+
{
306+
let owner = address_from_wire(&env, &owner)?;
307+
let receiver = address_from_wire(&env, &receiver)?;
308+
emit_withdraw_event(
309+
&env,
310+
&operator,
311+
&receiver,
312+
&owner,
313+
request_id,
314+
assets_out,
315+
shares_burned,
316+
);
317+
}
318+
Ok(())
274319
}
275320

276321
pub fn asset(env: Env) -> Result<Address, ContractError> {
@@ -428,7 +473,7 @@ fn deposit_with_min_internal(
428473
require_non_negative(assets)?;
429474
require_non_negative(min_shares_out)?;
430475
operator.require_auth();
431-
let shares = expect_i128_result(invoke_vault_execute(
476+
let receipt = decode_deposit_receipt(invoke_vault_execute(
432477
&env,
433478
VaultCommand::DepositWithMin {
434479
owner: operator.clone(),
@@ -437,8 +482,8 @@ fn deposit_with_min_internal(
437482
min_shares_out,
438483
},
439484
)?)?;
440-
emit_deposit_event(&env, &operator, &receiver, assets, shares);
441-
Ok(shares)
485+
emit_deposit_event(&env, &operator, &receiver, assets, receipt.shares_out);
486+
Ok(receipt.shares_out)
442487
}
443488

444489
pub(crate) fn read_vault_address(env: &Env) -> Result<Address, ContractError> {
@@ -468,7 +513,7 @@ pub(crate) fn read_share_token(env: &Env) -> Result<Address, ContractError> {
468513
pub(crate) fn invoke_vault_execute(
469514
env: &Env,
470515
command: VaultCommand,
471-
) -> Result<WireVaultCommandResult, ContractError> {
516+
) -> Result<Bytes, ContractError> {
472517
let vault_address = read_vault_address(env)?;
473518
let command = command.into_wire()?;
474519
let payload = Bytes::from_slice(env, &command.encode());
@@ -487,7 +532,7 @@ pub(crate) fn invoke_vault_execute(
487532
Err(Err(invoke_error)) => return Err(map_vault_invoke_error(invoke_error)),
488533
};
489534

490-
WireVaultCommandResult::decode(&bytes.to_alloc_vec()).map_err(Into::into)
535+
Ok(bytes)
491536
}
492537

493538
fn call_proxy_view_full(
@@ -570,25 +615,16 @@ where
570615
}
571616
}
572617

573-
fn expect_i128_result(result: WireVaultCommandResult) -> Result<i128, ContractError> {
574-
match result {
575-
WireVaultCommandResult::I128(value) => Ok(value),
576-
_ => Err(ContractError::VaultError),
577-
}
618+
fn decode_deposit_receipt(bytes: Bytes) -> Result<DepositReceipt, ContractError> {
619+
DepositReceipt::decode(&bytes.to_alloc_vec()).map_err(Into::into)
578620
}
579621

580-
fn expect_u64_result(result: WireVaultCommandResult) -> Result<u64, ContractError> {
581-
match result {
582-
WireVaultCommandResult::U64(value) => Ok(value),
583-
_ => Err(ContractError::VaultError),
584-
}
622+
fn decode_request_withdraw_receipt(bytes: Bytes) -> Result<RequestWithdrawReceipt, ContractError> {
623+
RequestWithdrawReceipt::decode(&bytes.to_alloc_vec()).map_err(Into::into)
585624
}
586625

587-
fn expect_unit_result(result: WireVaultCommandResult) -> Result<(), ContractError> {
588-
match result {
589-
WireVaultCommandResult::Unit | WireVaultCommandResult::ExecuteWithdrawStatus(_) => Ok(()),
590-
_ => Err(ContractError::VaultError),
591-
}
626+
fn decode_execute_withdraw_receipt(bytes: Bytes) -> Result<ExecuteWithdrawReceipt, ContractError> {
627+
ExecuteWithdrawReceipt::decode(&bytes.to_alloc_vec()).map_err(Into::into)
592628
}
593629

594630
fn require_self_operator(operator: &Address, owner: &Address) -> Result<(), ContractError> {
@@ -631,7 +667,34 @@ pub(crate) fn emit_redeem_request_event(
631667
);
632668
}
633669

670+
#[allow(deprecated)]
671+
pub(crate) fn emit_withdraw_event(
672+
env: &Env,
673+
sender: &Address,
674+
receiver: &Address,
675+
owner: &Address,
676+
request_id: u64,
677+
assets: u128,
678+
shares: u128,
679+
) {
680+
env.events().publish(
681+
(
682+
symbol_short!("Withdraw"),
683+
sender.clone(),
684+
receiver.clone(),
685+
owner.clone(),
686+
request_id,
687+
),
688+
(assets, shares),
689+
);
690+
}
691+
634692
fn address_to_wire(address: &Address) -> Result<AllocString, ContractError> {
635693
let raw = address.to_string().to_bytes().to_alloc_vec();
636694
AllocString::from_utf8(raw).map_err(|_| ContractError::InvalidInput)
637695
}
696+
697+
fn address_from_wire(env: &Env, value: &AllocString) -> Result<Address, ContractError> {
698+
validate_address_strkey(value.as_bytes()).map_err(|_| ContractError::InvalidInput)?;
699+
Ok(Address::from_str(env, value))
700+
}

contract/proxy-4626-soroban/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ pub use {
1212
contract::Soroban4626ProxyContract,
1313
error::ContractError,
1414
templar_soroban_shared_types::{
15-
ProxyPreviewFields, ProxyPreviewView, ProxyViewFields, ProxyViewResponse,
16-
VaultCommandResult,
15+
DepositReceipt, ExecuteWithdrawReceipt, ProxyPreviewFields, ProxyPreviewView,
16+
ProxyViewFields, ProxyViewResponse, RequestWithdrawReceipt,
1717
},
1818
};
1919

0 commit comments

Comments
 (0)