From fe09ae590a2a1e118e1b08b67aee139cb696a1f8 Mon Sep 17 00:00:00 2001 From: holygits Date: Wed, 13 Dec 2023 20:52:56 +0800 Subject: [PATCH] Accept and return price/amounts in decimal format --- Cargo.lock | 303 +++++++++++++++++++++++++++++++++++++++++----- Cargo.toml | 1 + README.md | 77 ++++++++++-- src/controller.rs | 33 +++-- src/types.rs | 193 +++++++++++++++++++++++------ 5 files changed, 521 insertions(+), 86 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 291cefb..2acfb85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,7 +75,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -188,7 +188,7 @@ dependencies = [ "actix-router", "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -415,7 +415,7 @@ dependencies = [ "arrayref", "base64 0.13.1", "bincode", - "borsh", + "borsh 0.9.3", "bytemuck", "solana-program", "thiserror", @@ -501,7 +501,7 @@ dependencies = [ "argh_shared", "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -601,7 +601,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -690,6 +690,18 @@ dependencies = [ "typenum", ] +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake3" version = "1.5.0" @@ -735,10 +747,20 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" dependencies = [ - "borsh-derive", + "borsh-derive 0.9.3", "hashbrown 0.11.2", ] +[[package]] +name = "borsh" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9897ef0f1bd2362169de6d7e436ea2237dc1085d7d1e4db75f4be34d86f309d1" +dependencies = [ + "borsh-derive 1.2.1", + "cfg_aliases", +] + [[package]] name = "borsh-derive" version = "0.9.3" @@ -752,6 +774,20 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "borsh-derive" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478b41ff04256c5c8330f3dfdaaae2a5cc976a8e75088bafa4625b0d0208de8c" +dependencies = [ + "once_cell", + "proc-macro-crate 2.0.1", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", + "syn_derive", +] + [[package]] name = "borsh-derive-internal" version = "0.9.3" @@ -823,6 +859,28 @@ dependencies = [ "serde", ] +[[package]] +name = "bytecheck" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "bytemuck" version = "1.13.0" @@ -840,7 +898,7 @@ checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -890,6 +948,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "chrono" version = "0.4.31" @@ -1270,7 +1334,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -1299,13 +1363,13 @@ dependencies = [ [[package]] name = "drift" version = "2.48.0" -source = "git+https://github.com/circuit-research/protocol-v2?branch=cargo-add-sdk#79f4491e7218430278a9a8b9356b511c2186648d" +source = "git+https://github.com/circuit-research/protocol-v2?branch=cargo-add-sdk#8d01d95956eb33a52b5ca767a317beacd662a2bd" dependencies = [ "anchor-lang", "anchor-spl", "arrayref", "base64 0.13.1", - "borsh", + "borsh 0.9.3", "bytemuck", "enumflags2", "num-derive", @@ -1330,6 +1394,7 @@ dependencies = [ "env_logger 0.10.1", "futures-util", "log", + "rust_decimal", "serde", "serde_json", "thiserror", @@ -1338,7 +1403,7 @@ dependencies = [ [[package]] name = "drift-sdk" version = "0.1.0" -source = "git+https://github.com/circuit-research/protocol-v2?branch=cargo-add-sdk#79f4491e7218430278a9a8b9356b511c2186648d" +source = "git+https://github.com/circuit-research/protocol-v2?branch=cargo-add-sdk#8d01d95956eb33a52b5ca767a317beacd662a2bd" dependencies = [ "anchor-lang", "drift", @@ -1459,7 +1524,7 @@ dependencies = [ "once_cell", "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -1592,6 +1657,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.29" @@ -1648,7 +1719,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -2567,7 +2638,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -2670,7 +2741,7 @@ name = "phoenix-v1" version = "0.2.4" source = "git+https://github.com/drift-labs/phoenix-v1?rev=4c65c9#4c65c97cd62493e27425ea2830447c833152bed1" dependencies = [ - "borsh", + "borsh 0.9.3", "bytemuck", "ellipsis-macros", "itertools 0.10.5", @@ -2754,7 +2825,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +dependencies = [ + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2 1.0.70", + "quote 1.0.33", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "version_check", ] [[package]] @@ -2775,6 +2879,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "pyth-client" version = "0.2.2" @@ -2861,6 +2985,12 @@ dependencies = [ "proc-macro2 1.0.70", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.7.3" @@ -3022,6 +3152,15 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rend" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd" +dependencies = [ + "bytecheck", +] + [[package]] name = "reqwest" version = "0.11.22" @@ -3096,6 +3235,34 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rkyv" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" +dependencies = [ + "bitvec", + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" +dependencies = [ + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 1.0.109", +] + [[package]] name = "rpassword" version = "6.0.1" @@ -3108,6 +3275,22 @@ dependencies = [ "winapi", ] +[[package]] +name = "rust_decimal" +version = "1.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06676aec5ccb8fc1da723cc8c0f9a46549f21ebb8753d3915c6c41db1e7f1dc4" +dependencies = [ + "arrayvec", + "borsh 1.2.1", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -3258,6 +3441,12 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "security-framework" version = "2.9.2" @@ -3313,7 +3502,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -3509,6 +3698,12 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + [[package]] name = "sized-chunks" version = "0.6.5" @@ -3866,8 +4061,8 @@ dependencies = [ "bincode", "bitflags 1.3.2", "blake3", - "borsh", - "borsh-derive", + "borsh 0.9.3", + "borsh-derive 0.9.3", "bs58 0.4.0", "bv", "bytemuck", @@ -3971,7 +4166,7 @@ dependencies = [ "base64 0.13.1", "bincode", "bitflags 1.3.2", - "borsh", + "borsh 0.9.3", "bs58 0.4.0", "bytemuck", "byteorder", @@ -4069,7 +4264,7 @@ dependencies = [ "Inflector", "base64 0.13.1", "bincode", - "borsh", + "borsh 0.9.3", "bs58 0.4.0", "lazy_static", "log", @@ -4186,7 +4381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbc000f0fdf1f12f99d77d398137c1751345b18c88258ce0f99b7872cf6c9bd6" dependencies = [ "assert_matches", - "borsh", + "borsh 0.9.3", "num-derive", "num-traits", "solana-program", @@ -4294,15 +4489,27 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.40" +version = "2.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" dependencies = [ "proc-macro2 1.0.70", "quote 1.0.33", "unicode-ident", ] +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2 1.0.70", + "quote 1.0.33", + "syn 2.0.41", +] + [[package]] name = "synstructure" version = "0.12.6" @@ -4336,6 +4543,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.8.1" @@ -4483,7 +4696,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -4569,9 +4782,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" @@ -4584,6 +4797,17 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -4610,7 +4834,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -4771,6 +4995,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" + [[package]] name = "vcpkg" version = "0.2.15" @@ -4831,7 +5061,7 @@ dependencies = [ "once_cell", "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", "wasm-bindgen-shared", ] @@ -4865,7 +5095,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5178,6 +5408,15 @@ dependencies = [ "unsize", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x509-parser" version = "0.14.0" @@ -5231,7 +5470,7 @@ checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba" dependencies = [ "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -5251,7 +5490,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2 1.0.70", "quote 1.0.33", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e372340..c2ef22a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ drift-sdk = { package = "drift-sdk", git = "https://github.com/circuit-research/ env_logger = "0.10.1" futures-util = "0.3.29" log = "0.4.20" +rust_decimal = "1.33.1" serde = { version = "1.0.193", features = ["derive"] } serde_json = "*" thiserror = "1.0.38" diff --git a/README.md b/README.md index 63bce42..ce6f76e 100644 --- a/README.md +++ b/README.md @@ -29,29 +29,88 @@ Options: ## Examples -Place Orders +### Get Market Info +```bash +~: curl localhost:8080/v2/markets +``` + +### Get Orders +```bash +~: curl localhost:8080/v2/orders +``` + +### Get Positions +```bash +~: curl localhost:8080/v2/positions +``` + +### Place Orders ```bash ~: curl localhost:8080/v2/orders -X POST -H 'content-type: application/json' -d '{ "orders": [{ "marketId": 1, "marketType": "spot", - "amount": 100000000, - "price": 40000000, + "amount": 1.23, + "price": 40.55, "postOnly": true, - "orderType": "limit" + "orderType": "limit", + "userOrderId": 101 + }, + { + "marketId": 0, + "marketType": "perp", + "amount": -1.05, + "price": 80, + "postOnly": true, + "orderType": "limit", + "userOrderId": 102 + }] +}' +``` + +### Modify Orders +like place orders but specify either `orderId` or `userOrderId` to indicate which order to modify +```bash +~: curl localhost:8080/v2/orders -X PATCH -H 'content-type: application/json' -d '{ + "orders": [{ + "marketId": 1, + "marketType": "spot", + "amount": 1.23, + "price": 40.55, + "postOnly": true, + "orderType": "limit", + "userOrderId": 5 }, { + "orderId": 555, "marketId": 0, "marketType": "perp", - "amount": -100000000, - "price": 70000000, + "amount": -1.05, + "price": 80, "postOnly": true, "orderType": "limit" }] }' ``` -Stream Orderbook +### Cancelling Orders +```bash +# cancel by market id +~: curl localhost:8080/v2/orders -X DELETE -H 'content-type: application/json' -d '{"market":{"id":1,"type":"perp"}}' +# cancel by order ids +~: curl localhost:8080/v2/orders -X DELETE -H 'content-type: application/json' -d '{"ids":[1,2,3,4]}' +# cancel by user assigned order ids +~: curl localhost:8080/v2/orders -X DELETE -H 'content-type: application/json' -d '{"userIds":[1,2,3,4]}' +# cancel all +~: curl localhost:8080/v2/orders -X DELETE -H 'content-type: application/json' ``` -curl localhost:8080/v2/orderbooks -N -X GET -H 'content-type: application/json' -d '{"market":{"id":3,"type":"perp"}}' -``` \ No newline at end of file + +### Stream Orderbook +```bash +~: curl localhost:8080/v2/orderbooks -N -X GET -H 'content-type: application/json' -d '{"market":{"id":3,"type":"perp"}}' +``` + + +# TODO: +- return error status for failed txs? +- allow empty json body on queries \ No newline at end of file diff --git a/src/controller.rs b/src/controller.rs index 95b0fbf..9a4fee7 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -12,7 +12,7 @@ use thiserror::Error; use crate::types::{ AllMarketsResponse, CancelOrdersRequest, GetOrderbookRequest, GetOrdersRequest, - GetOrdersResponse, GetPositionsRequest, GetPositionsResponse, ModifyOrdersRequest, + GetOrdersResponse, GetPositionsRequest, GetPositionsResponse, ModifyOrdersRequest, Order, PlaceOrdersRequest, }; @@ -32,6 +32,10 @@ pub struct AppState { } impl AppState { + /// Configured program/network context + pub fn context(&self) -> Context { + self.wallet.context() + } /// Configured drift user address pub fn user(&self) -> &Pubkey { self.wallet.user() @@ -43,9 +47,9 @@ impl AppState { pub async fn new(secret_key: &str, endpoint: &str, devnet: bool) -> Self { let wallet = Wallet::try_from_str( if devnet { - Context::Dev + Context::DevNet } else { - Context::Mainnet + Context::MainNet }, secret_key, ) @@ -80,7 +84,7 @@ impl AppState { let builder = TransactionBuilder::new(&self.wallet, &user_data); let tx = if let Some(market) = req.market { - builder.cancel_orders((market.id, market.market_type).into(), None) + builder.cancel_orders((market.id, market.market_type), None) } else if !req.user_ids.is_empty() { let order_ids = user_data .orders @@ -125,7 +129,7 @@ impl AppState { if let Some(ref market) = req.market { p.market_index == market.id && MarketType::Perp == market.market_type } else { - true + p.base_asset_amount != 0 } }) .map(|x| (*x).into()) @@ -149,14 +153,14 @@ impl AppState { true } }) - .map(Into::into) + .map(|o| Order::from_sdk_order(o, self.context())) .collect(), }) } pub fn get_markets(&self) -> AllMarketsResponse { - let spot = drift_sdk::constants::spot_markets(self.wallet.context()); - let perp = drift_sdk::constants::perp_markets(self.wallet.context()); + let spot = drift_sdk::constants::spot_market_configs(self.wallet.context()); + let perp = drift_sdk::constants::perp_market_configs(self.wallet.context()); AllMarketsResponse { spot: spot.iter().map(|x| (*x).into()).collect(), @@ -165,7 +169,11 @@ impl AppState { } pub async fn place_orders(&self, req: PlaceOrdersRequest) -> Result { - let orders = req.orders.into_iter().map(Into::into).collect(); + let orders = req + .orders + .into_iter() + .map(|o| o.to_order_params(self.context())) + .collect(); let tx = TransactionBuilder::new( &self.wallet, &self.client.get_account_data(self.user()).await?, @@ -184,14 +192,17 @@ impl AppState { let mut params = Vec::<(u32, OrderParams)>::with_capacity(req.orders.len()); for order in req.orders { if let Some(id) = order.order_id { - params.push((id, order.into())); + params.push((id, order.to_order_params(self.context()))); } else if order.user_order_id > 0 { if let Some(onchain_order) = user_data .orders .iter() .find(|x| x.user_order_id == order.user_order_id) { - params.push((onchain_order.order_id, order.into())); + params.push(( + onchain_order.order_id, + order.to_order_params(self.context()), + )); } } else { return Err(ControllerError::UnknownOrderId); diff --git a/src/types.rs b/src/types.rs index 44f8932..5ccd897 100644 --- a/src/types.rs +++ b/src/types.rs @@ -3,9 +3,15 @@ //! - wrappers for presenting drift program types with less implementation detail //! use drift_sdk::{ - constants::{PerpMarketConfig, SpotMarketConfig}, - types::{self as sdk_types, MarketType, OrderParams, PositionDirection, PostOnlyParam}, + constants::{ + spot_market_config_by_index, PerpMarketConfig, SpotMarketConfig, BASE_PRECISION, + QUOTE_PRECISION as PRICE_PRECISION, + }, + types::{ + self as sdk_types, Context, MarketType, OrderParams, PositionDirection, PostOnlyParam, + }, }; +use rust_decimal::Decimal; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[derive(Serialize, Deserialize)] @@ -18,21 +24,31 @@ pub struct Order { deserialize_with = "market_type_de" )] market_type: MarketType, - amount: i64, - price: u64, + amount: Decimal, + price: Decimal, post_only: bool, reduce_only: bool, user_order_id: u8, immediate_or_cancel: bool, } -impl From for Order { - fn from(value: sdk_types::Order) -> Self { +impl Order { + pub fn from_sdk_order(value: sdk_types::Order, context: Context) -> Self { + let amount = if let MarketType::Perp = value.market_type { + Decimal::from_i128_with_scale(value.base_asset_amount as i128, BASE_PRECISION.ilog10()) + } else { + let config = + spot_market_config_by_index(context, value.market_index).expect("market exists"); + Decimal::from_i128_with_scale( + value.base_asset_amount as i128, + config.precision_exp as u32, + ) + }; Order { market_id: value.market_index, market_type: value.market_type, - price: value.price, - amount: value.base_asset_amount as i64 * (1_i64 - (2 * value.direction as i64)), + price: Decimal::from_i128_with_scale(value.price as i128, PRICE_PRECISION.ilog10()), + amount, immediate_or_cancel: value.immediate_or_cancel, reduce_only: value.reduce_only, order_type: value.order_type, @@ -73,7 +89,7 @@ where #[derive(Serialize, Deserialize)] pub struct SpotPosition { - amount: u64, + amount: Decimal, #[serde(rename = "type")] balance_type: String, // deposit or borrow market_id: u16, @@ -82,38 +98,50 @@ pub struct SpotPosition { impl From for SpotPosition { fn from(value: sdk_types::SpotPosition) -> Self { Self { - amount: value.scaled_balance, + amount: Decimal::from_i128_with_scale( + value.scaled_balance as i128, + BASE_PRECISION.ilog10(), + ), market_id: value.market_index, - balance_type: value.balance_type.to_string(), + balance_type: if value.balance_type == Default::default() { + "deposit".into() + } else { + "borrow".into() + }, } } } #[derive(Serialize, Deserialize)] pub struct PerpPosition { - amount: i64, - average_entry: u64, + amount: Decimal, + average_entry: Decimal, market_id: u16, } impl From for PerpPosition { fn from(value: sdk_types::PerpPosition) -> Self { + let amount = + Decimal::from_i128_with_scale(value.base_asset_amount.into(), BASE_PRECISION.ilog10()); Self { - amount: value.base_asset_amount, + amount, market_id: value.market_index, - average_entry: (value.quote_entry_amount.abs() / value.base_asset_amount.abs()) as u64, + average_entry: Decimal::from_i128_with_scale( + (value.quote_entry_amount.abs() / value.base_asset_amount.abs().max(1)) as i128, + PRICE_PRECISION.ilog10(), + ), } } } pub type ModifyOrdersRequest = PlaceOrdersRequest; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct PlaceOrdersRequest { pub orders: Vec, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Default, Debug)] #[serde(rename_all = "camelCase")] pub struct PlaceOrder { market_id: u16, @@ -122,8 +150,8 @@ pub struct PlaceOrder { deserialize_with = "market_type_de" )] market_type: sdk_types::MarketType, - amount: i64, - price: u64, + amount: Decimal, + price: Decimal, /// 0 indicates it is not set (according to program) #[serde(default)] pub user_order_id: u8, @@ -162,27 +190,43 @@ where } } -impl From for sdk_types::OrderParams { - fn from(value: PlaceOrder) -> Self { +#[inline] +/// Convert decimal to unsigned fixed-point representation with `target` precision +fn scale_decimal_to_u64(x: Decimal, target: u32) -> u64 { + ((x.mantissa().unsigned_abs() * target as u128) / 10_u128.pow(x.scale())) as u64 +} + +impl PlaceOrder { + pub fn to_order_params(self, context: Context) -> OrderParams { + let target_scale = if let MarketType::Perp = self.market_type { + BASE_PRECISION as u32 + } else { + let config = + spot_market_config_by_index(context, self.market_id).expect("market exists"); + config.precision as u32 + }; + let base_amount = scale_decimal_to_u64(self.amount, target_scale); + let price = scale_decimal_to_u64(self.price, PRICE_PRECISION as u32); + OrderParams { - market_index: value.market_id, - market_type: value.market_type, - order_type: value.order_type, - base_asset_amount: value.amount.unsigned_abs(), - direction: if value.amount >= 0 { - PositionDirection::Long - } else { + market_index: self.market_id, + market_type: self.market_type, + order_type: self.order_type, + base_asset_amount: base_amount, + direction: if self.amount.is_sign_negative() { PositionDirection::Short + } else { + PositionDirection::Long }, - price: value.price, - immediate_or_cancel: value.immediate_or_cancel, - reduce_only: value.reduce_only, - post_only: if value.post_only { - PostOnlyParam::MustPostOnly + price, + immediate_or_cancel: self.immediate_or_cancel, + reduce_only: self.reduce_only, + post_only: if self.post_only { + PostOnlyParam::TryPostOnly } else { PostOnlyParam::None }, - user_order_id: value.user_order_id, + user_order_id: self.user_order_id, ..Default::default() } } @@ -210,12 +254,14 @@ impl Market { #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetPositionsRequest { + #[serde(default)] pub market: Option, } #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct GetOrdersRequest { + #[serde(default)] pub market: Option, } @@ -267,10 +313,13 @@ pub struct AllMarketsResponse { #[serde(rename_all = "camelCase")] pub struct CancelOrdersRequest { /// Market to cancel orders + #[serde(default)] pub market: Option, /// order Ids to cancel + #[serde(default)] pub ids: Vec, /// user assigned order Ids to cancel + #[serde(default)] pub user_ids: Vec, } @@ -279,3 +328,79 @@ pub struct CancelOrdersRequest { pub struct GetOrderbookRequest { pub market: Market, } + +#[cfg(test)] +mod tests { + use drift_sdk::types::{Context, MarketType}; + use std::str::FromStr; + + use crate::types::Order; + + use super::{Decimal, PlaceOrder}; + + #[test] + fn place_order_to_order() { + let cases = [ + ("0.1234", 123_400_000_u64), + ("123", 123_000_000_000), + ("-123.456", 123_456_000_000), + ("0.0034", 3_400_000), + ("10.0023", 10_002_300_000), + ("5.12345678911", 5_123_456_789), + ]; + for (input, expected) in cases { + let p = PlaceOrder { + amount: Decimal::from_str(input).unwrap(), + price: Decimal::from_str(input).unwrap(), + market_id: 0, + market_type: MarketType::Perp, + ..Default::default() + }; + let order_params = p.to_order_params(Context::DevNet); + assert_eq!(order_params.base_asset_amount, expected); + assert_eq!( + order_params.price, + expected / 1_000 // 1e9 - 1e6 + ); + } + } + + #[test] + fn place_order_to_order_spot() { + let cases = [ + ("0.1234", 123_400u64, 0_u16), + ("123", 12_300_000_000, 1), + ("1.23", 12_300_000_000, 1), + ("5.123456789", 512_345_678, 4), + ]; + for (input, expected, market_index) in cases { + let p = PlaceOrder { + amount: Decimal::from_str(input).unwrap(), + price: Decimal::from_str(input).unwrap(), + market_id: market_index, + market_type: MarketType::Spot, + ..Default::default() + }; + let order_params = p.to_order_params(Context::MainNet); + assert_eq!(order_params.base_asset_amount, expected); + } + } + + #[test] + fn order_from_sdk_order() { + let cases = [ + ("0.1234", 123_400u64, 0_u16), + ("123", 12_300_000_000, 1), + ("5.123456789", 512_345_678, 4), + ]; + for (input, expected, market_index) in cases { + let o = drift_sdk::types::Order { + base_asset_amount: 100000000, + price: 10000000, + ..Default::default() + }; + let gateway_order = Order::from_sdk_order(o, Context::MainNet); + // assert_eq!(gateway_order.amount, expected); + } + } +}