From c064cfd17030d750792aa8509d9d7133493c3a4c Mon Sep 17 00:00:00 2001 From: mvonsteinkirch Date: Sun, 5 Feb 2023 10:08:46 -0800 Subject: [PATCH] start refactoring to boilerplate searcher and add web3-toolkit crate --- README.md | 9 +- coingator/Cargo.toml | 17 ++ coingator/src/cexs/bbit.rs | 350 ++++++++++++++++++++++++ coingator/src/cexs/bmex.rs | 33 +++ coingator/src/cexs/bnb.rs | 33 +++ coingator/src/cexs/mod.rs | 3 + coingator/src/dexs/mempool.rs | 13 + coingator/src/dexs/mod.rs | 1 + coingator/src/lib.rs | 115 ++++++++ coingator/src/main.rs | 12 + coingator/src/market/coingecko.rs | 33 +++ coingator/src/market/mod.rs | 1 + coingator/src/searchers/mod.rs | 1 + coingator/src/searchers/searcher_one.rs | 13 + coingator/src/txs/calldata.rs | 13 + coingator/src/txs/decoder.rs | 13 + coingator/src/txs/mod.rs | 2 + coingator/src/utils.rs | 14 + 18 files changed, 672 insertions(+), 4 deletions(-) create mode 100644 coingator/Cargo.toml create mode 100644 coingator/src/cexs/bbit.rs create mode 100644 coingator/src/cexs/bmex.rs create mode 100644 coingator/src/cexs/bnb.rs create mode 100644 coingator/src/cexs/mod.rs create mode 100644 coingator/src/dexs/mempool.rs create mode 100644 coingator/src/dexs/mod.rs create mode 100644 coingator/src/lib.rs create mode 100644 coingator/src/main.rs create mode 100644 coingator/src/market/coingecko.rs create mode 100644 coingator/src/market/mod.rs create mode 100644 coingator/src/searchers/mod.rs create mode 100644 coingator/src/searchers/searcher_one.rs create mode 100644 coingator/src/txs/calldata.rs create mode 100644 coingator/src/txs/decoder.rs create mode 100644 coingator/src/txs/mod.rs create mode 100644 coingator/src/utils.rs diff --git a/README.md b/README.md index f5caa63..4d8ed92 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# šŸŠšŸ¤– COINGATOR: a rusty statistical searcher toolkit +# šŸŠšŸ¤– COINGATOR: a rusty statistical searcher boilerplate
@@ -15,18 +15,19 @@ -##### šŸŠ this package implements a toolkit with several utilities for running statistical strategies searchers. It's called coingator because it's a cute name. +##### šŸŠ this package implements a boilerplate with several utilities for running searchers with statistical strategies searchers. It's called coingator because it's a cute name. -##### šŸ“š for more details about this project, check my mirror post **[bot #3: coingator, a rusty statistical searcher toolkit]()**. +##### šŸ“š for more details about this project, check my mirror post **[bot #3: coingator, a rusty statistical searcher boilerplate]()**. +##### šŸ•¹ for the coolest web3 package (crate) in rust, check my [web3-toolkit-rs](https://github.com/go-outside-labs/web3-toolkit-rs) ##### šŸšØ disclaimer: i open-source my projects because i believe in the oss ethos. you might or might not profit from it, but this is not my problem. in the mev world, nobody is going to (explicitly) handle you alphas. i am not responsible for anything you do with my free code.
--- -## what's for you? +## features
diff --git a/coingator/Cargo.toml b/coingator/Cargo.toml new file mode 100644 index 0000000..47edcc6 --- /dev/null +++ b/coingator/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "coingator" +version = "0.1.0" +edition = "2021" +license = "MIT" +description="A statistical arbitrage seacher toolkit" + +[lib] +path = "src/lib.rs" + +[dependencies] +dotenv = "0.15.0" +reqwest = "0.11.10" +web3 = "0.18.0" +ethers = {version = "1.0.2", git = "https://github.com/gakonst/ethers-rs", features = ["ws", "rustls", "abigen"] } +tokio = { version = "1.5", features = ["macros", "rt-multi-thread"] } +rust-bybit = { version = "0.1.0-alpha.0", git = "https://github.com/yufuquant/rust-bybit.git" } diff --git a/coingator/src/cexs/bbit.rs b/coingator/src/cexs/bbit.rs new file mode 100644 index 0000000..12607ae --- /dev/null +++ b/coingator/src/cexs/bbit.rs @@ -0,0 +1,350 @@ +// bbit.rs - author: steinkirch + +use std::env; +use std::io::{self, Write}; +use bybit::linear::{PublicResponse, PublicWebSocketApiClient}; +use bybit::spot::ws::{OrderBookItem, PublicV2Response, PublicV2WebSocketApiClient, + PublicResponse as OtherPublicResponse, + PublicWebSocketApiClient as OtherPublicWebSocketApiClient, + PrivateResponse as OtherPrivateResponse, + PrivateWebSocketApiClient as OtherPrivateWebSocketApiClient}; +use bybit::inverse::{PrivateResponse, PrivateWebSocketApiClient, + PublicResponse as OtherOtherPublicResponse, + PublicWebSocketApiClient as OtherOtherPublicWebSocketApiClient}; + +struct PrivOrderBookItem(String, String); + + +//////////////////////////////// +// PUBLIC API +//////////////////////////////// + +pub async fn subscribe_coin() { + + let derivative = &env::var("DERIVATIVE").expect("ā›”ļø DERIVATIVE must be set on .env file"); + let mut client = PublicV2WebSocketApiClient::new(); + println!("šŸŠ subscribing to websockets for: {:?} \n", derivative); + + client.subscribe_depth(derivative, false); + client.subscribe_trade(derivative, false); + client.subscribe_book_ticker(derivative, false); + client.subscribe_realtimes(derivative, false); + + let callback = |res: PublicV2Response| match res { + PublicV2Response::Depth(res) => println!("āœ… depth: {:?}", res), + PublicV2Response::Kline(res) => println!("āœ… kline: {:?}", res), + PublicV2Response::Trade(res) => println!("āœ… trade: {:?}", res), + PublicV2Response::BookTicker(res) => println!("āœ… book ticker: {:?}", res), + PublicV2Response::Realtimes(res) => println!("āœ… realtimes: {:?}", res), + PublicV2Response::Pong(res) => println!("āœ… pong: {:?}", res), + PublicV2Response::Ping(res) => println!("āœ… ping: {:?}", res), + }; + + + match client.run(callback) { + Ok(_) => {} + Err(e) => println!("{}", e), + } +} + + +pub async fn subscribe_pairs() { + + let pairs = &env::var("DERIVATIVE_PAIR").expect("ā›”ļø DERIVATIVE_PAIR must be set on .env file"); + let symbols: Vec<&str> = pairs.split(",").collect(); + println!("šŸŠ subscribing to websockets for: {:?} \n", symbols); + + let mut client = PublicWebSocketApiClient::new(); + + client.subscribe_order_book_l2_25(&symbols); + client.subscribe_order_book_l2_200(&symbols); + client.subscribe_trade(&symbols); + client.subscribe_instrument_info(&symbols); + client.subscribe_kline(&symbols, "1"); + client.subscribe_liquidation(&symbols); + + let callback = |res: PublicResponse| match res { + PublicResponse::OrderBookL2Snapshot(res) => println!("āœ… order book L2 snapshot: {:?}", res), + PublicResponse::OrderBookL2Delta(res) => println!("āœ… order book L2 Ī”: {:?}", res), + PublicResponse::Trade(res) => println!("āœ… trade: {:?}", res), + PublicResponse::InstrumentInfoSnapshot(res) => { + println!("āœ… instrument info snapshot: {:?}", res) + } + PublicResponse::InstrumentInfoDelta(res) => { + println!("āœ… instrument info Ī”: {:?}", res) + } + PublicResponse::Kline(res) => println!("āœ… k-line: {:?}", res), + PublicResponse::Liquidation(res) => println!("āœ… liquidation: {:?}", res), + }; + + + match client.run(callback) { + Ok(_) => {} + Err(e) => println!("{}", e), + } +} + + +pub async fn subscribe_spot () { + + let derivative = &env::var("DERIVATIVE").expect("ā›”ļø DERIVATIVE must be set on .env file"); + let mut client = OtherPublicWebSocketApiClient::new(); + + println!("šŸŠ subscribing to websockets for: {:?} \n", derivative); + + let stdout = io::stdout(); + let mut handle = io::BufWriter::new(stdout); + let mut latest_price: String = String::new(); + let mut direction = "šŸ”ŗ"; + let mut asks: Vec = Vec::new(); + let mut bids: Vec = Vec::new(); + + client.subscribe_trade(derivative, false); + client.subscribe_diff_depth(derivative, false); + + let callback = |res: OtherPublicResponse| { + match res { + OtherPublicResponse::Trade(res) => { + let price = res.data[0].p.to_owned(); + if price < latest_price { + direction = "šŸ”»"; + } else if price > latest_price { + direction = "šŸ”ŗ"; + } + latest_price = price + } + OtherPublicResponse::Depth(res) => { + res.data[0].a.iter().for_each(|&OrderBookItem(price, qty)| { + asks.push(PrivOrderBookItem(price.to_owned(), qty.to_owned())); + }); + res.data[0].b.iter().for_each(|&OrderBookItem(price, qty)| { + bids.push(PrivOrderBookItem(price.to_owned(), qty.to_owned())); + }); + } + + OtherPublicResponse::DiffDepth(res) => { + + //////////// + // ASKS + //////////// + let a = &res.data[0].a; + let mut i: usize = 0; + let mut j: usize = 0; + + while i < a.len() { + let OrderBookItem(price, qty) = a[i]; + + while j < asks.len() { + let item = &mut asks[j]; + let item_price: &str = &item.0; + if price < item_price { + asks.insert(j, PrivOrderBookItem(price.to_owned(), qty.to_owned())); + i += 1; + j += 1; + break; + } + + if price == item_price { + if qty != "0" { + item.1 = qty.to_owned(); + i += 1; + j += 1; + } else { + asks.remove(j); + i += 1; + } + break; + } + + j += 1; + } + + if j == asks.len() { + a.iter().skip(i).for_each(|&OrderBookItem(price, qty)| { + asks.push(PrivOrderBookItem(price.to_owned(), qty.to_owned())); + }); + break; + } + } + + //////////// + // BIDS + //////////// + let b = &res.data[0].b; + let mut i: usize = 0; + let mut j: usize = 0; + + while i < b.len() { + let OrderBookItem(price, qty) = b[i]; + + while j < bids.len() { + let item = &mut bids[j]; + let item_price: &str = &item.0; + if price > item_price { + bids.insert(j, PrivOrderBookItem(price.to_owned(), qty.to_owned())); + i += 1; + j += 1; + break; + } + + if price == item_price { + if qty != "0" { + item.1 = qty.to_owned(); + i += 1; + j += 1; + } else { + bids.remove(j); + i += 1; + } + break; + } + + j += 1; + } + + if j == bids.len() { + b.iter().skip(i).for_each(|&OrderBookItem(price, qty)| { + bids.push(PrivOrderBookItem(price.to_owned(), qty.to_owned())); + }); + break; + } + } + } + _ => {} + } + + //////////// + // ASKS + //////////// + write!(handle, "\nāœØšŸŠ {} orderbook\n\n", derivative).unwrap(); + write!(handle, "{:<20} {:<20}\n", "šŸ’° price", "šŸ› quantity").unwrap(); + let mut asks_10 = asks.iter().take(10).collect::>().clone(); + asks_10.reverse(); + asks_10.iter().for_each(|item| { + write!(handle, "{:<20} {:<20}\n", item.0, item.1).unwrap(); + }); + write!(handle, "\n{} {}\n\n", direction, latest_price).unwrap(); + bids.iter().take(10).for_each(|item| { + write!(handle, "{:<20} {:<20}\n", item.0, item.1).unwrap(); + }); + handle.flush().unwrap(); + }; + + match client.run(callback) { + Ok(_) => {} + Err(e) => println!("{}", e), + } + +} + + +pub async fn subscribe_perpetual() { + + let mut client = OtherOtherPublicWebSocketApiClient::new(); + + let pairs = &env::var("DERIVATIVE_PAIR").expect("ā›”ļø DERIVATIVE_PAIR must be set on .env file"); + let symbols: Vec<&str> = pairs.split(",").collect(); + + println!("šŸŠ subscribing to websockets for: {:?} \n", pairs); + + client.subscribe_order_book_l2_25(&symbols); + client.subscribe_order_book_l2_200(&symbols); + client.subscribe_trade(&symbols); + client.subscribe_insurance(&symbols); + client.subscribe_instrument_info(&symbols); + client.subscribe_kline(&symbols, "1"); + client.subscribe_liquidation(&symbols); + + let callback = |res: OtherOtherPublicResponse| match res { + + OtherOtherPublicResponse::OrderBookL2Snapshot(res) => println!("āœ… orderbook L2 snapshot: {:?}", res), + OtherOtherPublicResponse::OrderBookL2Delta(res) => println!("āœ… orderbook L2 Ī”: {:?}", res), + OtherOtherPublicResponse::Trade(res) => println!("āœ… trade: {:?}", res), + OtherOtherPublicResponse::Insurance(res) => println!("āœ… insurance: {:?}", res), + OtherOtherPublicResponse::PerpetualInstrumentInfoSnapshot(res) => { + println!("āœ… perpetual instrument info snapshot: {:?}", res) + } + OtherOtherPublicResponse::PerpetualInstrumentInfoDelta(res) => { + println!("āœ… perpetual instrument info Ī”: {:?}", res) + } + OtherOtherPublicResponse::FuturesInstrumentInfoSnapshot(res) => { + println!("āœ… futures instrument info snapshot: {:?}", res) + } + OtherOtherPublicResponse::FuturesInstrumentInfoDelta(res) => { + println!("āœ… futures instrument info Ī”: {:?}", res) + } + OtherOtherPublicResponse::Kline(res) => println!("āœ… k-line: {:?}", res), + OtherOtherPublicResponse::Liquidation(res) => println!("āœ… liquidation: {:?}", res), + }; + + match client.run(callback) { + Ok(_) => {} + Err(e) => println!("{}", e), + } + +} + + +//////////////////////////////// +// PRIVATE API +// +// These methods are not being +// used by the main app. +// +//////////////////////////////// + +pub async fn subscribe_exec() { + + let api_key = &env::var("BYBIT_API_KEY").expect("ā›”ļø BYBIT_API_KEY must be set on .env file"); + let api_secret = &env::var("BYBIT_API_SECRET").expect("ā›”ļø BYBIT_API_SECRET must be set on .env file"); + + println!("šŸŠ subscribing to private executions websockets: \n"); + + let client = OtherPrivateWebSocketApiClient::builder().testnet() + .build_with_credentials(&api_key, &api_secret); + + let callback = |res: OtherPrivateResponse| match res { + OtherPrivateResponse::ExecutionReportSequence(seq) => println!("āœ… execution report: {:?}", seq), + OtherPrivateResponse::TicketInfoSequence(seq) => println!("āœ… ticket info: {:?}", seq), + OtherPrivateResponse::OutboundAccountInfoSequence(seq) => { + println!("āœ… outbound account info: {:?}", seq) + }, + OtherPrivateResponse::Pong(res) => println!("āœ… pong: {:?}", res), + OtherPrivateResponse::Ping(res) => println!("āœ… ping: {:?}", res), + }; + + match client.run(callback) { + Ok(_) => {} + Err(e) => println!("{}", e), + } +} + +pub async fn subscribe_positions() { + + let api_key = &env::var("BYBIT_API_KEY").expect("ā›”ļø BYBIT_API_KEY must be set on .env file"); + let api_secret = &env::var("BYBIT_API_SECRET").expect("ā›”ļø BYBIT_API_SECRET must be set on .env file"); + + println!("šŸŠ subscribing to private positions websockets: \n"); + + let mut client = PrivateWebSocketApiClient::new(api_key, api_secret); + + client.subscribe_position(); + client.subscribe_execution(); + client.subscribe_order(); + client.subscribe_stop_order(); + client.subscribe_wallet(); + + let callback = |res: PrivateResponse| match res { + PrivateResponse::Position(res) => println!("āœ… position: {:?}", res), + PrivateResponse::Execution(res) => println!("āœ… execution: {:?}", res), + PrivateResponse::Order(res) => println!("āœ… order: {:?}", res), + PrivateResponse::StopOrder(res) => println!("āœ… stop order: {:?}", res), + PrivateResponse::Wallet(res) => println!("āœ… wallet: {:?}", res), + }; + + match client.run(callback) { + Ok(_) => {} + Err(e) => println!("{}", e), + } +} + diff --git a/coingator/src/cexs/bmex.rs b/coingator/src/cexs/bmex.rs new file mode 100644 index 0000000..2706eb9 --- /dev/null +++ b/coingator/src/cexs/bmex.rs @@ -0,0 +1,33 @@ +// bmex.rs - author: steinkirch + +use std::env; +use std::io::{self, Write}; + + +pub async fn subscribe_coin() { + + println!("to be implemented"); + +} + + +pub async fn subscribe_pairs() { + + println!("to be implemented"); + +} + + +pub async fn subscribe_spot () { + + println!("to be implemented"); + +} + + +pub async fn subscribe_perpetual() { + + println!("to be implemented"); + +} + diff --git a/coingator/src/cexs/bnb.rs b/coingator/src/cexs/bnb.rs new file mode 100644 index 0000000..21b1bba --- /dev/null +++ b/coingator/src/cexs/bnb.rs @@ -0,0 +1,33 @@ +// bnb.rs - author: steinkirch + +use std::env; +use std::io::{self, Write}; + + +pub async fn subscribe_coin() { + + println!("to be implemented"); + +} + + +pub async fn subscribe_pairs() { + + println!("to be implemented"); + +} + + +pub async fn subscribe_spot () { + + println!("to be implemented"); + +} + + +pub async fn subscribe_perpetual() { + + println!("to be implemented"); + +} + diff --git a/coingator/src/cexs/mod.rs b/coingator/src/cexs/mod.rs new file mode 100644 index 0000000..873e4b8 --- /dev/null +++ b/coingator/src/cexs/mod.rs @@ -0,0 +1,3 @@ +pub mod bbit; +pub mod bmex; +pub mod bnb; \ No newline at end of file diff --git a/coingator/src/dexs/mempool.rs b/coingator/src/dexs/mempool.rs new file mode 100644 index 0000000..d35d87c --- /dev/null +++ b/coingator/src/dexs/mempool.rs @@ -0,0 +1,13 @@ +// mempool.rs - author: steinkirch + +use std::env; +use std::io::{self, Write}; + + +pub async fn run() { + + println!("to be implemented"); + +} + + diff --git a/coingator/src/dexs/mod.rs b/coingator/src/dexs/mod.rs new file mode 100644 index 0000000..09c1c0b --- /dev/null +++ b/coingator/src/dexs/mod.rs @@ -0,0 +1 @@ +pub mod mempool; diff --git a/coingator/src/lib.rs b/coingator/src/lib.rs new file mode 100644 index 0000000..d140c0c --- /dev/null +++ b/coingator/src/lib.rs @@ -0,0 +1,115 @@ +// lib.rs - author: steinkirch + + +use std::env; + +pub mod cexs; +pub mod dexs; +pub mod txs; +pub mod market; +pub mod searchers; + + +use crate::cexs::bbit; +use crate::cexs::bmex; +use crate::cexs::bnb; +use crate::dexs::mempool; +use crate::txs::decoder; +use crate::txs::calldata; +use crate::market::coingecko; +use crate::searchers::searcher_one; + + + + +pub async fn run() { + + println!("\nšŸŠ welcome to coingator šŸŖ™. type your option:\n"); + println!("āž” 1: sub to public topics for a derivative"); + println!("āž” 2: sub to public topics for a pair of derivatives"); + println!("āž” 3: sub to public topics for inverse perpetual contracts"); + println!("āž” 4: sub to public topics for spot local orderbook"); + println!("āž” 5: sub to public mempool"); + println!("āž” 6: run searcher boilerplate"); + println!("āž” 7: run calldata decoder"); + println!("āž” 8: run tx decoder"); + + // create an argument input + let mut input = String::new(); + + // read the input + std::io::stdin().read_line(&mut input).unwrap(); + + // create a vector of arguments + let mut args = input.split_whitespace(); + + // get the command + let command = args.next().unwrap(); + + // select which cex to use + let source = &env::var("SOURCE").expect("ā›”ļø SOURCE must be set on .env file"); + + if source == "bybit" { + match command { + "1" => bbit::subscribe_coin().await, + "2" => bbit::subscribe_pairs().await, + "3" => bbit::subscribe_perpetual().await, + "4" => bbit::subscribe_spot().await, + "5" => mempool::run().await, + "6" => searcher_one::run().await, + "7" => calldata::run().await, + "8" => decoder::run().await, + _ => println!("command not found: {}", command), + } + + } else if source == "coingecko" { + match command { + "1" => coingecko::subscribe_coin().await, + "2" => coingecko::subscribe_pairs().await, + "3" => coingecko::subscribe_perpetual().await, + "4" => coingecko::subscribe_spot().await, + "5" => mempool::run().await, + "6" => searcher_one::run().await, + "7" => calldata::run().await, + "8" => decoder::run().await, + _ => println!("command not found: {}", command), + } + + + } else if source == "binance" { + match command { + "1" => bnb::subscribe_coin().await, + "2" => bnb::subscribe_pairs().await, + "3" => bnb::subscribe_perpetual().await, + "4" => bnb::subscribe_spot().await, + "5" => mempool::run().await, + "6" => searcher_one::run().await, + "7" => calldata::run().await, + "8" => decoder::run().await, + _ => println!("command not found: {}", command), + } + + } else if source == "bitmex" { + match command { + "1" => bmex::subscribe_coin().await, + "2" => bmex::subscribe_pairs().await, + "3" => bmex::subscribe_perpetual().await, + "5" => mempool::run().await, + "6" => searcher_one::run().await, + "7" => calldata::run().await, + "8" => decoder::run().await, + _ => println!("command not found: {}", command), + } + + } else { + match command { + "5" => mempool::run().await, + "6" => searcher_one::run().await, + "7" => calldata::run().await, + "8" => decoder::run().await, + _ => println!("command not found: {}", command), + } + } + +} + diff --git a/coingator/src/main.rs b/coingator/src/main.rs new file mode 100644 index 0000000..6a5682d --- /dev/null +++ b/coingator/src/main.rs @@ -0,0 +1,12 @@ +// main.rs - author: steinkirch + +use coingator::run; + + +#[tokio::main] +async fn main() { + + dotenv::dotenv().expect("failed to read .env file"); + run().await; + +} diff --git a/coingator/src/market/coingecko.rs b/coingator/src/market/coingecko.rs new file mode 100644 index 0000000..2053658 --- /dev/null +++ b/coingator/src/market/coingecko.rs @@ -0,0 +1,33 @@ +// coingecko.rs - author: steinkirch + +use std::env; +use std::io::{self, Write}; + + +pub async fn subscribe_coin() { + + println!("to be implemented"); + +} + + +pub async fn subscribe_pairs() { + + println!("to be implemented"); + +} + + +pub async fn subscribe_spot () { + + println!("to be implemented"); + +} + + +pub async fn subscribe_perpetual() { + + println!("to be implemented"); + +} + diff --git a/coingator/src/market/mod.rs b/coingator/src/market/mod.rs new file mode 100644 index 0000000..94185ca --- /dev/null +++ b/coingator/src/market/mod.rs @@ -0,0 +1 @@ +pub mod coingecko; diff --git a/coingator/src/searchers/mod.rs b/coingator/src/searchers/mod.rs new file mode 100644 index 0000000..4666330 --- /dev/null +++ b/coingator/src/searchers/mod.rs @@ -0,0 +1 @@ +pub mod searcher_one; diff --git a/coingator/src/searchers/searcher_one.rs b/coingator/src/searchers/searcher_one.rs new file mode 100644 index 0000000..5aeec47 --- /dev/null +++ b/coingator/src/searchers/searcher_one.rs @@ -0,0 +1,13 @@ +// searcher_one.rs - author: steinkirch + +use std::env; +use std::io::{self, Write}; + + +pub async fn run() { + + println!("to be implemented"); + +} + + diff --git a/coingator/src/txs/calldata.rs b/coingator/src/txs/calldata.rs new file mode 100644 index 0000000..9c4410a --- /dev/null +++ b/coingator/src/txs/calldata.rs @@ -0,0 +1,13 @@ +// calldata.rs - author: steinkirch + +use std::env; +use std::io::{self, Write}; + + +pub async fn run() { + + println!("to be implemented"); + +} + + diff --git a/coingator/src/txs/decoder.rs b/coingator/src/txs/decoder.rs new file mode 100644 index 0000000..7d437b1 --- /dev/null +++ b/coingator/src/txs/decoder.rs @@ -0,0 +1,13 @@ +// decoder.rs - author: steinkirch + +use std::env; +use std::io::{self, Write}; + + +pub async fn run() { + + println!("to be implemented"); + +} + + diff --git a/coingator/src/txs/mod.rs b/coingator/src/txs/mod.rs new file mode 100644 index 0000000..66ec8c5 --- /dev/null +++ b/coingator/src/txs/mod.rs @@ -0,0 +1,2 @@ +pub mod decoder; +pub mod calldata; diff --git a/coingator/src/utils.rs b/coingator/src/utils.rs new file mode 100644 index 0000000..7d36fdb --- /dev/null +++ b/coingator/src/utils.rs @@ -0,0 +1,14 @@ +// utils.rs - author: steinkirch + +use hex; +use ring::hmac; + + +pub fn sign(secret: &str, msg: &str) -> String { + + let key = hmac::Key::new(hmac::HMAC_SHA256, secret.as_bytes()); + let tag = hmac::sign(&key, msg.as_bytes()); + hex::encode(tag.as_ref()) + +} +