Skip to content

Commit 1fa4f3a

Browse files
authored
A0-1140: e2e tests for simple_dex (#723)
* Add simple_dex to deploy.sh * Expose out_given_in in simple_dex * Add e2e test for simple_dex * Fix clippy * Test dex swap rejection due to slippage
1 parent 8e4dcf7 commit 1fa4f3a

File tree

9 files changed

+369
-19
lines changed

9 files changed

+369
-19
lines changed

aleph-client/src/contract/convertible_value.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::ops::Deref;
22

3-
use anyhow::{bail, Result};
3+
use anyhow::{anyhow, bail, Result};
44
use contract_transcode::Value;
55
use sp_core::crypto::Ss58Codec;
66

@@ -71,3 +71,36 @@ impl TryFrom<ConvertibleValue> for AccountId {
7171
}
7272
}
7373
}
74+
75+
impl<T> TryFrom<ConvertibleValue> for Result<T>
76+
where
77+
ConvertibleValue: TryInto<T, Error = anyhow::Error>,
78+
{
79+
type Error = anyhow::Error;
80+
81+
fn try_from(value: ConvertibleValue) -> Result<Result<T>, Self::Error> {
82+
if let Value::Tuple(tuple) = &value.0 {
83+
match tuple.ident() {
84+
Some(x) if x == "Ok" => {
85+
if tuple.values().count() == 1 {
86+
let item =
87+
ConvertibleValue(tuple.values().next().unwrap().clone()).try_into()?;
88+
return Ok(Ok(item));
89+
} else {
90+
bail!("Unexpected number of elements in Ok variant: {:?}", &value);
91+
}
92+
}
93+
Some(x) if x == "Err" => {
94+
if tuple.values().count() == 1 {
95+
return Ok(Err(anyhow!(value.to_string())));
96+
} else {
97+
bail!("Unexpected number of elements in Err variant: {:?}", &value);
98+
}
99+
}
100+
_ => (),
101+
}
102+
}
103+
104+
bail!("Expected {:?} to be an Ok(_) or Err(_) tuple.", value);
105+
}
106+
}

aleph-client/src/contract/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,15 @@ impl ContractInstance {
9696
conn: &C,
9797
message: &str,
9898
) -> Result<ConvertibleValue> {
99-
self.contract_read(conn, message, &[])
99+
self.contract_read::<C, String>(conn, message, &[])
100100
}
101101

102102
/// Reads the value of a read-only call via RPC.
103-
pub fn contract_read<C: AnyConnection>(
103+
pub fn contract_read<C: AnyConnection, S: AsRef<str> + Debug>(
104104
&self,
105105
conn: &C,
106106
message: &str,
107-
args: &[&str],
107+
args: &[S],
108108
) -> Result<ConvertibleValue> {
109109
let payload = self.encode(message, args)?;
110110
let request = self.contract_read_request(&payload);
@@ -121,15 +121,15 @@ impl ContractInstance {
121121

122122
/// Executes a 0-argument contract call.
123123
pub fn contract_exec0(&self, conn: &SignedConnection, message: &str) -> Result<()> {
124-
self.contract_exec(conn, message, &[])
124+
self.contract_exec::<String>(conn, message, &[])
125125
}
126126

127127
/// Executes a contract call.
128-
pub fn contract_exec(
128+
pub fn contract_exec<S: AsRef<str> + Debug>(
129129
&self,
130130
conn: &SignedConnection,
131131
message: &str,
132-
args: &[&str],
132+
args: &[S],
133133
) -> Result<()> {
134134
let data = self.encode(message, args)?;
135135
let xt = compose_extrinsic!(
@@ -164,7 +164,7 @@ impl ContractInstance {
164164
})
165165
}
166166

167-
fn encode(&self, message: &str, args: &[&str]) -> Result<Vec<u8>> {
167+
fn encode<S: AsRef<str> + Debug>(&self, message: &str, args: &[S]) -> Result<Vec<u8>> {
168168
ContractMessageTranscoder::new(&self.ink_project).encode(message, args)
169169
}
170170

contracts/scripts/test.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ CONTRACTS_PATH=$(pwd)/contracts
77
EARLY_BIRD_SPECIAL=$(jq --raw-output ".early_bird_special" < "$CONTRACTS_PATH"/addresses.json)
88
THE_PRESSIAH_COMETH=$(jq --raw-output ".the_pressiah_cometh" < "$CONTRACTS_PATH"/addresses.json)
99
BACK_TO_THE_FUTURE=$(jq --raw-output ".back_to_the_future" < "$CONTRACTS_PATH"/addresses.json)
10+
SIMPLE_DEX=$(jq --raw-output ".simple_dex" < "$CONTRACTS_PATH"/addresses.json)
1011

1112
pushd "$E2E_PATH"
1213

1314
RUST_LOG="aleph_e2e_client=info" cargo run --release -- \
15+
--test-cases simple_dex \
1416
--test-cases marketplace \
1517
--test-cases button_game_reset \
1618
--test-cases early_bird_special \
@@ -19,9 +21,11 @@ RUST_LOG="aleph_e2e_client=info" cargo run --release -- \
1921
--early-bird-special "$EARLY_BIRD_SPECIAL" \
2022
--the-pressiah-cometh "$THE_PRESSIAH_COMETH" \
2123
--back-to-the-future "$BACK_TO_THE_FUTURE" \
24+
--simple-dex "$SIMPLE_DEX" \
2225
--button-game-metadata ../contracts/button/target/ink/metadata.json \
2326
--ticket-token-metadata ../contracts/ticket_token/target/ink/metadata.json \
2427
--reward-token-metadata ../contracts/game_token/target/ink/metadata.json \
25-
--marketplace-metadata ../contracts/marketplace/target/ink/metadata.json
28+
--marketplace-metadata ../contracts/marketplace/target/ink/metadata.json \
29+
--simple-dex-metadata ../contracts/simple_dex/target/ink/metadata.json
2630

2731
exit $?

e2e-tests/src/cases.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::{
1515
force_new_era as test_force_new_era, marketplace as test_marketplace,
1616
points_basic as test_points_basic, points_stake_change as test_points_stake_change,
1717
schedule_doomed_version_change_and_verify_finalization_stopped as test_schedule_doomed_version_change_and_verify_finalization_stopped,
18-
schedule_version_change as test_schedule_version_change,
18+
schedule_version_change as test_schedule_version_change, simple_dex as test_simple_dex,
1919
staking_era_payouts as test_staking_era_payouts,
2020
staking_new_validator as test_staking_new_validator,
2121
the_pressiah_cometh as test_the_pressiah_cometh, token_transfer as test_token_transfer,
@@ -72,6 +72,7 @@ pub fn possible_test_cases() -> PossibleTestCases {
7272
("back_to_the_future", test_back_to_the_future as TestCase),
7373
("the_pressiah_cometh", test_the_pressiah_cometh as TestCase),
7474
("marketplace", test_marketplace as TestCase),
75+
("simple_dex", test_simple_dex as TestCase),
7576
("ban_automatic", test_ban_automatic as TestCase),
7677
("ban_manual", test_ban_manual as TestCase),
7778
(

e2e-tests/src/config.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ pub struct TestCaseParams {
8181
#[clap(long)]
8282
pub the_pressiah_cometh: Option<String>,
8383

84+
/// Address of the simple dex contract.
85+
#[clap(long)]
86+
pub simple_dex: Option<String>,
87+
8488
/// Path to the button game metadata file. Only used by button tests.
8589
#[clap(long)]
8690
pub button_game_metadata: Option<String>,
@@ -97,6 +101,10 @@ pub struct TestCaseParams {
97101
#[clap(long)]
98102
pub marketplace_metadata: Option<String>,
99103

104+
/// Path to the simple_dex metadata file. Only used by button tests.
105+
#[clap(long)]
106+
pub simple_dex_metadata: Option<String>,
107+
100108
/// Version for the VersionUpgrade test.
101109
#[clap(long)]
102110
pub upgrade_to_version: Option<u32>,

e2e-tests/src/test/button_game/contracts.rs

Lines changed: 119 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,121 @@ use sp_core::crypto::{AccountId32 as AccountId, Ss58Codec};
66

77
use crate::Config;
88

9+
/// A wrapper around the simple dex contract.
10+
///
11+
/// The methods on this type match contract methods.
12+
#[derive(Debug)]
13+
pub(super) struct SimpleDexInstance {
14+
contract: ContractInstance,
15+
}
16+
17+
impl<'a> From<&'a SimpleDexInstance> for &'a ContractInstance {
18+
fn from(dex: &'a SimpleDexInstance) -> Self {
19+
&dex.contract
20+
}
21+
}
22+
23+
impl<'a> From<&'a SimpleDexInstance> for AccountId {
24+
fn from(dex: &'a SimpleDexInstance) -> Self {
25+
dex.contract.address().clone()
26+
}
27+
}
28+
29+
impl SimpleDexInstance {
30+
pub fn new(config: &Config) -> Result<Self> {
31+
let dex_address = config
32+
.test_case_params
33+
.simple_dex
34+
.clone()
35+
.context("Simple dex address not set.")?;
36+
let dex_address = AccountId::from_string(&dex_address)?;
37+
let metadata_path = config
38+
.test_case_params
39+
.simple_dex_metadata
40+
.clone()
41+
.context("Simple dex metadata not set")?;
42+
43+
Ok(Self {
44+
contract: ContractInstance::new(dex_address, &metadata_path)?,
45+
})
46+
}
47+
48+
pub fn add_swap_pair(
49+
&self,
50+
conn: &SignedConnection,
51+
from: AccountId,
52+
to: AccountId,
53+
) -> Result<()> {
54+
self.contract
55+
.contract_exec(conn, "add_swap_pair", &[&from.to_string(), &to.to_string()])
56+
}
57+
58+
pub fn deposit(
59+
&self,
60+
conn: &SignedConnection,
61+
amounts: &[(&PSP22TokenInstance, Balance)],
62+
) -> Result<()> {
63+
let deposits = amounts
64+
.iter()
65+
.map(|(token, amount)| {
66+
let address: AccountId = (*token).try_into()?;
67+
Ok(format!("deposits ({:}, {:})", address, amount))
68+
})
69+
.collect::<Result<Vec<String>>>()?;
70+
71+
self.contract
72+
.contract_exec(conn, "deposit", &[format!("[{:}]", deposits.join(","))])
73+
}
74+
75+
pub fn out_given_in<C: AnyConnection>(
76+
&self,
77+
conn: &C,
78+
token_in: &PSP22TokenInstance,
79+
token_out: &PSP22TokenInstance,
80+
amount_token_in: Balance,
81+
min_amount_token_out: Option<Balance>,
82+
) -> Result<Balance> {
83+
let token_in: AccountId = token_in.into();
84+
let token_out: AccountId = token_out.into();
85+
86+
self.contract
87+
.contract_read(
88+
conn,
89+
"out_given_in",
90+
&[
91+
token_in.to_string(),
92+
token_out.to_string(),
93+
amount_token_in.to_string(),
94+
min_amount_token_out.map_or("None".to_string(), |x| format!("Some({:})", x)),
95+
],
96+
)?
97+
.try_into()?
98+
}
99+
100+
pub fn swap(
101+
&self,
102+
conn: &SignedConnection,
103+
token_in: &PSP22TokenInstance,
104+
amount_token_in: Balance,
105+
token_out: &PSP22TokenInstance,
106+
min_amount_token_out: Balance,
107+
) -> Result<()> {
108+
let token_in: AccountId = token_in.into();
109+
let token_out: AccountId = token_out.into();
110+
111+
self.contract.contract_exec(
112+
conn,
113+
"swap",
114+
&[
115+
token_in.to_string(),
116+
token_out.to_string(),
117+
amount_token_in.to_string(),
118+
min_amount_token_out.to_string(),
119+
],
120+
)
121+
}
122+
}
123+
9124
/// A wrapper around a button game contract.
10125
///
11126
/// The methods on this type match contract methods.
@@ -99,15 +214,15 @@ impl PSP22TokenInstance {
99214
self.contract.contract_exec(
100215
conn,
101216
"PSP22::transfer",
102-
&[to.to_string().as_str(), amount.to_string().as_str(), "0x00"],
217+
&[to.to_string(), amount.to_string(), "0x00".to_string()],
103218
)
104219
}
105220

106221
pub fn mint(&self, conn: &SignedConnection, to: &AccountId, amount: Balance) -> Result<()> {
107222
self.contract.contract_exec(
108223
conn,
109224
"PSP22Mintable::mint",
110-
&[to.to_string().as_str(), amount.to_string().as_str()],
225+
&[to.to_string(), amount.to_string()],
111226
)
112227
}
113228

@@ -120,13 +235,13 @@ impl PSP22TokenInstance {
120235
self.contract.contract_exec(
121236
conn,
122237
"PSP22::approve",
123-
&[spender.to_string().as_str(), value.to_string().as_str()],
238+
&[spender.to_string(), value.to_string()],
124239
)
125240
}
126241

127242
pub fn balance_of(&self, conn: &Connection, account: &AccountId) -> Result<Balance> {
128243
self.contract
129-
.contract_read(conn, "PSP22::balance_of", &[account.to_string().as_str()])?
244+
.contract_read(conn, "PSP22::balance_of", &[account.to_string()])?
130245
.try_into()
131246
}
132247
}

0 commit comments

Comments
 (0)