diff --git a/contracts/simple_dex/Cargo.lock b/contracts/simple_dex/Cargo.lock index 04f35808e2..0ce3811097 100644 --- a/contracts/simple_dex/Cargo.lock +++ b/contracts/simple_dex/Cargo.lock @@ -40,6 +40,27 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitvec" version = "1.0.1" @@ -87,6 +108,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87c5fdd0166095e1d463fc6cc01aa8ce547ad77a4e84d42eb6762b084e28067e" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "camino" version = "1.1.1" @@ -217,6 +244,21 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "fs2" version = "0.4.3" @@ -484,6 +526,15 @@ dependencies = [ "synstructure", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "itertools" version = "0.10.3" @@ -505,6 +556,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.132" @@ -734,6 +791,38 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" +dependencies = [ + "bit-set", + "bitflags", + "byteorder", + "lazy_static", + "num-traits", + "quick-error 2.0.1", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.21" @@ -779,12 +868,57 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + [[package]] name = "rlibc" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc874b127765f014d792f16763a81245ab80500e2ad921ed4ee9e82481ee08fe" +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error 1.2.3", + "tempfile", + "wait-timeout", +] + [[package]] name = "ryu" version = "1.0.11" @@ -927,6 +1061,7 @@ dependencies = [ "ink_storage", "openbrush", "parity-scale-codec", + "proptest", "scale-info", ] @@ -971,6 +1106,20 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + [[package]] name = "thiserror" version = "1.0.34" @@ -1042,6 +1191,15 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/contracts/simple_dex/Cargo.toml b/contracts/simple_dex/Cargo.toml index 8318177cca..cfca965111 100644 --- a/contracts/simple_dex/Cargo.toml +++ b/contracts/simple_dex/Cargo.toml @@ -21,6 +21,9 @@ openbrush = { git = "https://github.com/Supercolony-net/openbrush-contracts.git" access_control = { path = "../access_control", default-features = false, features = ["ink-as-dependency"] } game_token = { path = "../game_token", default-features = false, features = ["ink-as-dependency"] } +[dev-dependencies] +proptest = "1.0" + [lib] name = "simple_dex" path = "lib.rs" @@ -47,4 +50,4 @@ std = [ ink-as-dependency = [] [profile.dev] -codegen-units = 16 \ No newline at end of file +codegen-units = 16 diff --git a/contracts/simple_dex/lib.rs b/contracts/simple_dex/lib.rs index c848b08eb1..d73051912b 100644 --- a/contracts/simple_dex/lib.rs +++ b/contracts/simple_dex/lib.rs @@ -375,8 +375,22 @@ mod simple_dex { let balance_token_in = self.balance_of(token_in, this)?; let balance_token_out = self.balance_of(token_out, this)?; + Self::_out_given_in( + amount_token_in, + balance_token_in, + balance_token_out, + self.swap_fee_percentage, + ) + } + + fn _out_given_in( + amount_token_in: Balance, + balance_token_in: Balance, + balance_token_out: Balance, + swap_fee_percentage: Balance, + ) -> Result { let op0 = amount_token_in - .checked_mul(self.swap_fee_percentage) + .checked_mul(swap_fee_percentage) .ok_or(DexError::Arithmethic)?; let op1 = balance_token_in @@ -395,6 +409,8 @@ mod simple_dex { balance_token_out .checked_sub(op4) + // If the division is not even, leave the 1 unit of dust in the exchange instead of paying it out. + .and_then(|result| result.checked_sub(if op3 % op2 > 0 { 1 } else { 0 })) .ok_or(DexError::Arithmethic) } @@ -509,4 +525,30 @@ mod simple_dex { SimpleDex::new() } } + + #[cfg(test)] + mod test { + use proptest::prelude::*; + + use super::*; + + proptest! { + #[test] + fn rounding_benefits_dex( + balance_token_a in 1..1000u128, + balance_token_b in 1..1000u128, + pay_token_a in 1..1000u128, + fee_percentage in 0..10u128 + ) { + let get_token_b = + SimpleDex::_out_given_in(pay_token_a, balance_token_a, balance_token_b, fee_percentage).unwrap(); + let balance_token_a = balance_token_a + pay_token_a; + let balance_token_b = balance_token_b - get_token_b; + let get_token_a = + SimpleDex::_out_given_in(get_token_b, balance_token_b, balance_token_a, fee_percentage).unwrap(); + + assert!(get_token_a <= pay_token_a); + } + } + } }