Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add out asset refund case #108

Merged
merged 2 commits into from
Dec 8, 2024
Merged
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
6 changes: 0 additions & 6 deletions packages/types/src/stream/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,6 @@ pub enum QueryMsg {
/// Returns a stream's current state.
#[returns(StreamResponse)]
Stream {},
/// Returns list of streams paginated by `start_after` and `limit`.
// #[returns(StreamsResponse)]
// ListStreams {
// start_after: Option<u64>,
// limit: Option<u32>,
// },
/// Returns current state of a position.
#[returns(PositionResponse)]
Position { owner: String },
Expand Down
136 changes: 136 additions & 0 deletions tests/src/tests/streamswap_tests/finalize_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,4 +652,140 @@ mod finalize_stream_tests {
.unwrap();
assert_eq!(contract_balance.len(), 0);
}

#[test]
fn out_amount_refund() {
let Suite {
mut app,
test_accounts,
stream_swap_code_id,
stream_swap_controller_code_id,
vesting_code_id,
} = SuiteBuilder::default().build();
let start_time = app.block_info().time.plus_seconds(100);
let end_time = app.block_info().time.plus_seconds(200);
let bootstrapping_start_time = app.block_info().time.plus_seconds(50);
let msg = get_controller_inst_msg(stream_swap_code_id, vesting_code_id, &test_accounts);
let controller_address = app
.instantiate_contract(
stream_swap_controller_code_id,
test_accounts.admin.clone(),
&msg,
&[],
"Controller".to_string(),
None,
)
.unwrap();
let create_stream_msg = CreateStreamMsgBuilder::new(
"Stream Swap tests",
test_accounts.creator_1.as_ref(),
coin(1_000_000, "out_denom"),
"in_denom",
bootstrapping_start_time,
start_time,
end_time,
)
.build();

// This case handles the scenario where the `out_amount` is refunded to the creator.
// For this to happen, the allocated amount for the stream must remain unspent during the stream's lifetime.
// If, at any point during the stream phase, no subscribers are active,
// a portion of the `out_amount` may remain unused and will be left in the contract.
// This unspent amount should be refunded separately to the creator.

let res = app
.execute_contract(
test_accounts.creator_1.clone(),
controller_address.clone(),
&create_stream_msg,
&[coin(100, "fee_denom"), coin(1_000_000, "out_denom")],
)
.unwrap();
let stream_swap_contract_address: String = get_contract_address_from_res(res);
let subscribe_msg = StreamSwapExecuteMsg::Subscribe {};

app.set_block(BlockInfo {
height: 1_100,
time: start_time,
chain_id: "test".to_string(),
});

// First subscription
let _res = app
.execute_contract(
test_accounts.subscriber_1.clone(),
Addr::unchecked(stream_swap_contract_address.clone()),
&subscribe_msg,
&[coin(200, "in_denom")],
)
.unwrap();

// Update environment time to end_time
app.set_block(BlockInfo {
height: 1_100,
time: end_time.minus_seconds(1),
chain_id: "test".to_string(),
});

// Subscriber 1 withdraws from the stream
let withdraw_msg = StreamSwapExecuteMsg::Withdraw { cap: None };
let _res = app
.execute_contract(
test_accounts.subscriber_1.clone(),
Addr::unchecked(stream_swap_contract_address.clone()),
&withdraw_msg,
&[],
)
.unwrap();

//Update environment time to end_time
app.set_block(BlockInfo {
height: 1_100,
time: end_time,
chain_id: "test".to_string(),
});

// Finalize the stream
let finalized_msg = StreamSwapExecuteMsg::FinalizeStream {
new_treasury: None,
create_pool: None,
salt: None,
};
let res = app
.execute_contract(
test_accounts.creator_1.clone(),
Addr::unchecked(stream_swap_contract_address.clone()),
&finalized_msg,
&[],
)
.unwrap();

let stream_swap_funds = get_funds_from_res(res);
assert_eq!(
stream_swap_funds,
vec![
(
String::from(test_accounts.creator_1.clone()),
Coin {
denom: "out_denom".to_string(),
amount: Uint128::new(10000)
},
),
(
String::from(test_accounts.creator_1.clone()),
Coin {
denom: "in_denom".to_string(),
amount: Uint128::new(197)
},
),
(
String::from(test_accounts.admin.clone()),
Coin {
denom: "in_denom".to_string(),
amount: Uint128::new(1)
},
)
]
);
}
}
98 changes: 98 additions & 0 deletions tests/src/tests/streamswap_tests/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod pool {
use cosmwasm_std::{coin, Addr, BlockInfo, Coin, Uint256};
use cw_multi_test::Executor;
use cw_utils::NativeBalance;
use streamswap_controller::error::ContractError as ControllerError;
use streamswap_types::controller::{CreatePool, PoolConfig};
use streamswap_types::stream::ExecuteMsg as StreamSwapExecuteMsg;
use streamswap_types::stream::QueryMsg as StreamSwapQueryMsg;
Expand Down Expand Up @@ -486,4 +487,101 @@ mod pool {
.collect();
assert_eq!(res_funds, expected_res);
}

#[test]
fn pool_validations() {
let Suite {
mut app,
test_accounts,
stream_swap_code_id,
stream_swap_controller_code_id,
vesting_code_id,
} = SuiteBuilder::default().build();

let msg = get_controller_inst_msg(stream_swap_code_id, vesting_code_id, &test_accounts);
let controller_address = app
.instantiate_contract(
stream_swap_controller_code_id,
test_accounts.admin.clone(),
&msg,
&[],
"Controller".to_string(),
None,
)
.unwrap();

let start_time = app.block_info().time.plus_seconds(100);
let end_time = app.block_info().time.plus_seconds(200);
let bootstrapping_start_time = app.block_info().time.plus_seconds(50);
let out_supply = 1_000_000u128;
let out_denom = "out_denom";

let out_coin = coin(out_supply, out_denom);
let pool_creation_fee = coin(1000000, "fee_denom");
let stream_creation_fee = coin(100, "fee_denom");
let in_denom = "in_denom";

// Pool amount 0 case
let create_stream_msg = CreateStreamMsgBuilder::new(
"stream",
test_accounts.creator_1.as_ref(),
out_coin.clone(),
in_denom,
bootstrapping_start_time,
start_time,
end_time,
)
.threshold(Uint256::from(100u128))
.pool_config(PoolConfig::ConcentratedLiquidity {
out_amount_clp: Uint256::zero(),
})
.build();

let res = app
.execute_contract(
test_accounts.creator_1.clone(),
controller_address.clone(),
&create_stream_msg,
&[
pool_creation_fee.clone(),
out_coin.clone(),
stream_creation_fee.clone(),
],
)
.unwrap_err();
let err = res.downcast::<ControllerError>().unwrap();
assert_eq!(err, ControllerError::InvalidPoolOutAmount {});

// Pool amount greater than out supply case
let create_stream_msg = CreateStreamMsgBuilder::new(
"stream",
test_accounts.creator_1.as_ref(),
out_coin.clone(),
in_denom,
bootstrapping_start_time,
start_time,
end_time,
)
.threshold(Uint256::from(100u128))
.pool_config(PoolConfig::ConcentratedLiquidity {
out_amount_clp: Uint256::from(out_supply + 1),
})
.build();

let res = app
.execute_contract(
test_accounts.creator_1.clone(),
controller_address.clone(),
&create_stream_msg,
&[
pool_creation_fee.clone(),
out_coin.clone(),
stream_creation_fee,
],
)
.unwrap_err();

let err = res.downcast::<ControllerError>().unwrap();
assert_eq!(err, ControllerError::InvalidPoolOutAmount {});
}
}
Loading