Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 46 additions & 2 deletions crates/common/src/deposit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub enum DepositError {
#[error(transparent)]
FloatError(#[from] FloatError),

#[error("vault-id 0 is vaultless and cannot be used for deposits")]
ZeroVaultId,

#[cfg(not(target_family = "wasm"))]
#[error(transparent)]
WriteTransactionError(#[from] crate::write_tx::WriteTransactionError),
Expand All @@ -36,9 +39,13 @@ pub struct DepositArgs {
}

impl TryFrom<DepositArgs> for deposit4Call {
type Error = FloatError;
type Error = DepositError;

fn try_from(val: DepositArgs) -> Result<Self, Self::Error> {
if val.vault_id == B256::ZERO {
return Err(DepositError::ZeroVaultId);
}

Ok(deposit4Call {
token: val.token,
vaultId: val.vault_id,
Expand Down Expand Up @@ -97,9 +104,9 @@ impl DepositArgs {
transaction_args: TransactionArgs,
transaction_status_changed: S,
) -> Result<(), DepositError> {
let deposit_call: deposit4Call = self.clone().try_into()?;
let (ledger_client, _) = transaction_args.clone().try_into_ledger_client().await?;

let deposit_call: deposit4Call = self.clone().try_into()?;
let tx_request = transaction_args
.try_into_transaction_request(deposit_call, transaction_args.raindex_address)?;

Expand All @@ -108,3 +115,40 @@ impl DepositArgs {
Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_deposit_call_rejects_zero_vault_id() {
let args = DepositArgs {
token: Address::ZERO,
vault_id: B256::ZERO,
amount: Float::parse("1".to_string()).unwrap(),
decimals: 18,
};

assert!(matches!(
deposit4Call::try_from(args),
Err(DepositError::ZeroVaultId)
));
}

#[cfg(not(target_family = "wasm"))]
#[tokio::test]
async fn test_execute_deposit_rejects_zero_vault_id_before_transaction_setup() {
let args = DepositArgs {
token: Address::ZERO,
vault_id: B256::ZERO,
amount: Float::parse("1".to_string()).unwrap(),
decimals: 18,
};

assert!(matches!(
args.execute_deposit(TransactionArgs::default(), |_| {})
.await,
Err(DepositError::ZeroVaultId)
));
}
}
8 changes: 4 additions & 4 deletions crates/common/src/raindex_client/vaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,13 +502,13 @@ impl RaindexVault {
) -> Result<RaindexVaultCalldatas, RaindexError> {
self.validate_amount(amount)?;

let approval = self.build_approval_calldata(amount).await?;

let deposit_args = self.get_deposit_args(amount);
let deposit = Bytes::copy_from_slice(&deposit4Call::try_from(deposit_args)?.abi_encode());

let deposit_call = deposit4Call::try_from(deposit_args)?;
let withdraw = self.build_withdraw_calldata(amount).await?;

let approval = self.build_approval_calldata(amount).await?;
let deposit = Bytes::copy_from_slice(&deposit_call.abi_encode());

Ok(RaindexVaultCalldatas {
approval,
deposit,
Expand Down
56 changes: 52 additions & 4 deletions crates/common/src/raindex_client/vaults_list.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use alloy::{primitives::Bytes, sol_types::SolCall};
use alloy::{
primitives::{Bytes, U256},
sol_types::SolCall,
};
use rain_math_float::Float;
use raindex_bindings::Raindex::multicallCall;
use serde::{Deserialize, Serialize};
Expand All @@ -21,7 +24,10 @@ impl RaindexVaultsList {
pub fn get_withdrawable_vaults(&self) -> Vec<&RaindexVault> {
self.0
.iter()
.filter(|vault| vault.balance().gt(*ZERO_FLOAT).unwrap_or(false))
.filter(|vault| {
vault.raw_vault_id() != U256::ZERO
&& vault.balance().gt(*ZERO_FLOAT).unwrap_or(false)
})
.collect()
}

Expand Down Expand Up @@ -298,6 +304,28 @@ mod tests {
})
}

fn get_vault3_json() -> Value {
json!({
"id": "0x0345",
"owner": "0x0000000000000000000000000000000000000000",
"vaultId": "0",
"balance": "0x0000000000000000000000000000000000000000000000000000000000000020",
"token": {
"id": "token2",
"address": "0x12e605bc104e93b45e1ad99f9e555f659051c2bb",
"name": "Token 2",
"symbol": "TKN2",
"decimals": "18"
},
"raindex": {
"id": "0x0000000000000000000000000000000000000000"
},
"ordersAsOutput": [],
"ordersAsInput": [],
"balanceChanges": []
})
}

async fn get_vaults() -> Vec<RaindexVault> {
let sg_server = MockServer::start_async().await;
sg_server.mock(|when, then| {
Expand All @@ -312,7 +340,7 @@ mod tests {
when.path("/sg2");
then.status(200).json_body_obj(&json!({
"data": {
"vaults": [get_vault2_json()]
"vaults": [get_vault2_json(), get_vault3_json()]
}
}));
});
Expand All @@ -337,7 +365,7 @@ mod tests {
#[tokio::test]
async fn test_get_vaults_not_empty() {
let vaults_list = RaindexVaultsList::new(get_vaults().await);
assert_eq!(vaults_list.0.len(), 2);
assert_eq!(vaults_list.0.len(), 3);
}

#[tokio::test]
Expand All @@ -346,6 +374,26 @@ mod tests {
let withdrawable_vaults = vaults_list.get_withdrawable_vaults();
assert_eq!(withdrawable_vaults.len(), 1);
assert_eq!(withdrawable_vaults[0].id().to_string(), "0x0234"); // vault2 has non-zero balance
assert_ne!(withdrawable_vaults[0].vault_id(), U256::ZERO);
}

#[tokio::test]
async fn test_zero_vault_get_calldatas_rejects_before_allowance_read() {
let vaults_list = RaindexVaultsList::new(get_vaults().await);
let zero_vault = vaults_list
.items()
.into_iter()
.find(|vault| vault.vault_id() == U256::ZERO)
.unwrap();

let err = zero_vault
.get_calldatas(&Float::parse("1".to_string()).unwrap())
.await
.unwrap_err();

assert!(err
.to_string()
.contains("vault-id 0 is vaultless and cannot be used for deposits"));
}

#[tokio::test]
Expand Down
4 changes: 1 addition & 3 deletions crates/common/src/raindex_order_builder/order_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,7 @@ impl RaindexOrderBuilder {
vault_id: vault_id.into(),
decimals,
};
let calldata = deposit4Call::try_from(deposit_args)
.map_err(crate::deposit::DepositError::from)?
.abi_encode();
let calldata = deposit4Call::try_from(deposit_args)?.abi_encode();
calldatas.push(Bytes::copy_from_slice(&calldata));
}

Expand Down
60 changes: 54 additions & 6 deletions crates/common/src/withdraw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use serde::{Deserialize, Serialize};

use rain_math_float::Float;
use raindex_bindings::IRaindexV6::withdraw4Call;
use std::convert::TryFrom;

#[derive(Clone, Deserialize, Serialize, Debug)]
pub struct WithdrawArgs {
Expand All @@ -17,14 +18,22 @@ pub struct WithdrawArgs {
pub target_amount: Float,
}

impl From<WithdrawArgs> for withdraw4Call {
fn from(val: WithdrawArgs) -> Self {
withdraw4Call {
impl TryFrom<WithdrawArgs> for withdraw4Call {
type Error = WritableTransactionExecuteError;

fn try_from(val: WithdrawArgs) -> Result<Self, Self::Error> {
if val.vault_id == B256::ZERO {
return Err(WritableTransactionExecuteError::InvalidArgs(
"vault-id 0 is vaultless and cannot be used for withdrawals".to_string(),
));
}

Ok(withdraw4Call {
token: val.token,
vaultId: val.vault_id,
targetAmount: val.target_amount.get_inner(),
tasks: vec![],
}
})
}
}

Expand All @@ -35,9 +44,9 @@ impl WithdrawArgs {
transaction_args: TransactionArgs,
transaction_status_changed: S,
) -> Result<(), WritableTransactionExecuteError> {
let withdraw_call: withdraw4Call = self.clone().try_into()?;
let (ledger_client, _) = transaction_args.clone().try_into_ledger_client().await?;

let withdraw_call: withdraw4Call = self.clone().into();
let tx_request = transaction_args
.try_into_transaction_request(withdraw_call, transaction_args.raindex_address)?;

Expand All @@ -47,7 +56,46 @@ impl WithdrawArgs {
}

pub async fn get_withdraw_calldata(&self) -> Result<Vec<u8>, WritableTransactionExecuteError> {
let withdraw_call: withdraw4Call = self.clone().into();
let withdraw_call: withdraw4Call = self.clone().try_into()?;
Ok(withdraw_call.abi_encode())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_withdraw_call_rejects_zero_vault_id() {
let args = WithdrawArgs {
token: Address::ZERO,
vault_id: B256::ZERO,
target_amount: Float::parse("1".to_string()).unwrap(),
};

let err = withdraw4Call::try_from(args).unwrap_err();
assert_eq!(
err.to_string(),
"Invalid input args: vault-id 0 is vaultless and cannot be used for withdrawals"
);
}

#[cfg(not(target_family = "wasm"))]
#[tokio::test]
async fn test_execute_rejects_zero_vault_id_before_transaction_setup() {
let args = WithdrawArgs {
token: Address::ZERO,
vault_id: B256::ZERO,
target_amount: Float::parse("1".to_string()).unwrap(),
};

let err = args
.execute(TransactionArgs::default(), |_| {})
.await
.unwrap_err();
assert_eq!(
err.to_string(),
"Invalid input args: vault-id 0 is vaultless and cannot be used for withdrawals"
);
}
}
Loading