Skip to content
Open
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
1,225 changes: 657 additions & 568 deletions Cargo.lock

Large diffs are not rendered by default.

46 changes: 23 additions & 23 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,34 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
argon2 = "0.5.2"
axum = { version = "0.8.3", features = ["ws", "macros"] }
argon2 = "0.5.3"
axum = { version = "0.8.9", features = ["ws", "macros"] }
dotenvy = "0.15.7"
hex = "0.4.3"
jwt-simple = { version = "0.12.12", default-features = false, features = ["pure-rust"] }
lettre = {version = "0.11.4", features = ["rustls-tls"]}
openssl = {version = "0.10.72", features = ["vendored"]}
rand = "0.10"
serde = {version = "1.0.195", features = ["derive"]}
jwt-simple = { version = "0.13.0", default-features = false, features = ["pure-rust"] }
lettre = {version = "0.11.23", features = ["rustls-tls"]}
openssl = {version = "0.10.81", features = ["vendored"]}
rand = { version = "0.10.2", features = ["sys_rng"] }
serde = {version = "1.0.229", features = ["derive"]}
siwe = { git = "https://github.com/futex-labs/siwe", rev = "1459e6ab72932bfdba79f4f950000cedebf86496", features = ["alloy", "serde"] }
sqlx = {version = "0.8", features = ["postgres", "macros", "runtime-tokio", "tls-rustls", "time", "uuid"]}
time = {version = "0.3.36" , features = ["serde"]}
tokio = {version = "1.47.1", features = ["rt-multi-thread", "macros"]}
tokio-test = "0.4.3"
tower-http = {version = "0.6.9", features = ["cors"]}
sqlx = {version = "0.9", features = ["postgres", "macros", "runtime-tokio", "tls-rustls", "time", "uuid"]}
time = {version = "0.3.55" , features = ["serde"]}
tokio = {version = "1.53.1", features = ["rt-multi-thread", "macros"]}
tokio-test = "0.4.5"
tower-http = {version = "0.7.0", features = ["cors"]}
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
alloy = {version = "2.0.4", features = ["node-bindings", "network", "rpc-types"]}
thiserror = "2.0.18"
mimalloc = "0.1.45"
tracing-subscriber = "0.3.23"
alloy = {version = "2.3.0", features = ["node-bindings", "network", "rpc-types"]}
thiserror = "2.0.19"
mimalloc = "0.1.52"
url = "2.5.8"
reqwest = { version = "0.13.3", features = ["json", "cookies", "stream"] }
http-body-util = "0.1"
serde_json = "1.0.140"
tokio-tungstenite = { version = "0.29.0", features = ["native-tls"] }
http = "1.3.1"
futures-util = "0.3.31"
sstr = {version = "0.3.0", features = ["sqlx-postgres", "serde"]}
reqwest = { version = "0.13.4", features = ["json", "cookies", "stream"] }
http-body-util = "0.1.4"
serde_json = "1.0.151"
tokio-tungstenite = { version = "0.30.0", features = ["native-tls"] }
http = "1.5.0"
futures-util = "0.3.33"
sstr = { version = "0.3.1", features = ["sqlx-postgres", "serde"]}

[dev-dependencies]
alloy = {version = "2.0", features = ["node-bindings", "network", "rpc-types", "signer-local"]}
Expand Down
2 changes: 1 addition & 1 deletion benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fn generate_body() -> (Vec<u8>, String) {
.to_owned();

let key = dotenvy::var("D_D_CLOUD_API_KEY").expect("D_D_CLOUD_API_KEY");
let provider = format!("https://api.cloud.developerdao.com/rpc/base/{}", &key);
let provider = format!("https://api.cloud.developerdao.com/rpc/base/{}", key);

(body, provider)
}
Expand Down
2 changes: 2 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::middleware::ingress_limits::body_size_limiter;
use crate::middleware::jwt_auth::verify_jwt;
#[cfg(not(feature = "dev"))]
use crate::middleware::rpc_service::validate_subscription_and_update_user_calls;
use crate::routes::activate::new_activation_code;
use crate::routes::payment::{cancel, downgrade, upgrade};
use crate::routes::relayer::router::{
route_arb, route_base, route_bsc, route_eth, route_op, route_poly, route_solana, route_sui,
Expand Down Expand Up @@ -139,6 +140,7 @@ async fn main() {
)
.route("/api/register", post(register_user))
.route("/api/activate", post(activate_account))
.route("/api/activate/retry/{email}", post(new_activation_code))
.route("/api/login", post(user_login))
.route("/api/login/siwe", post(user_login_siwe))
.route("/api/recovery", post(update_password))
Expand Down
96 changes: 89 additions & 7 deletions src/routes/activate.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
use crate::{database::types::RELATIONAL_DATABASE, routes::register::generate_verification_code};
use axum::{Json, http::StatusCode, response::IntoResponse};
use crate::{
EmailLogin,
database::types::RELATIONAL_DATABASE,
routes::{register::generate_verification_code, types::SERVER_EMAIL},
};
use axum::{Json, extract::Path, http::StatusCode, response::IntoResponse};
use lettre::{
Message, SmtpTransport, Transport,
message::{Mailbox, header::ContentType},
transport::smtp::{self, authentication::Credentials},
};
use serde::{Deserialize, Serialize};
use thiserror::Error;

Expand All @@ -15,6 +24,62 @@ pub struct ActivationCode {
pub activated: bool,
}

#[tracing::instrument]
pub async fn new_activation_code(
Path(email): Path<sstr::Str<256>>,
) -> Result<impl IntoResponse, ActivationError> {
let db = RELATIONAL_DATABASE.get().unwrap();

let code = sqlx::query_as!(
ActivationCode,
"SELECT verificationCode, activated FROM Customers where email = $1",
&email
)
.fetch_optional(db)
.await?
.ok_or_else(|| ActivationError::UserNotFound)?;

if code.activated {
Err(ActivationError::AlreadyActivated)?;
}

let new_code: String = generate_verification_code(8);
sqlx::query!(
"UPDATE Customers SET verificationCode = $1 WHERE email = $2",
&new_code,
&email
)
.execute(db)
.await?;

let server_email_info: &'static EmailLogin = SERVER_EMAIL.get().unwrap();
let email_credentials = Credentials::new(
server_email_info.address.to_string(),
server_email_info.password.to_string(),
);

let server_mailbox: Mailbox =
format!("Developer DAO RPC <{}>", server_email_info.address).parse()?;
let user_email = email.parse()?;

let email = Message::builder()
.from(server_mailbox)
.to(user_email)
.subject("D_D RPC Verification Code")
.header(ContentType::TEXT_PLAIN)
.body(format!("Your verification code is: {new_code}"))?;

let mailer = SmtpTransport::starttls_relay("smtp.gmail.com")?
.credentials(email_credentials)
.build();

let _: smtp::response::Response = mailer
.send(&email)
.expect("Failed to send verification email)");

Ok((StatusCode::OK).into_response())
}

#[tracing::instrument]
pub async fn activate_account(
Json(payload): Json<ActivationRequest>,
Expand Down Expand Up @@ -59,14 +124,31 @@ pub enum ActivationError {
InvalidCode,
#[error("This account is already activated. Please login.")]
AlreadyActivated,
#[error(transparent)]
EmailError(#[from] lettre::transport::smtp::Error),
#[error(transparent)]
AddressError(#[from] lettre::address::AddressError),
#[error(transparent)]
LettreError(#[from] lettre::error::Error),
}

impl IntoResponse for ActivationError {
fn into_response(self) -> axum::response::Response {
(
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
self.to_string(),
)
.into_response()
match self {
ActivationError::UserNotFound => {
(StatusCode::NOT_FOUND, self.to_string()).into_response()
}
ActivationError::InvalidCode => {
(StatusCode::UNAUTHORIZED, self.to_string()).into_response()
}
ActivationError::AlreadyActivated => {
(axum::http::StatusCode::FORBIDDEN, self.to_string()).into_response()
}
_ => (
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
self.to_string(),
)
.into_response(),
}
}
}
11 changes: 6 additions & 5 deletions src/routes/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ pub enum RegisterUserError {
SmtpError(#[from] lettre::transport::smtp::Error),
#[error(transparent)]
JoinError(#[from] JoinError),
#[error("The email supplied is invalid")]
InvalidEmail,
}

impl IntoResponse for RegisterUserError {
Expand Down Expand Up @@ -159,7 +161,6 @@ pub mod test {
};
use axum::{Router, routing::post};
use dotenvy::dotenv;
use jwt_simple::reexports::rand::SeedableRng;
use lettre::{
Message, Transport,
message::{Mailbox, header::ContentType},
Expand All @@ -173,10 +174,10 @@ pub mod test {

#[test]
fn hash_test() {
use argon2::password_hash::rand_core::OsRng;

let hashed_pass: String = {
let salt = SaltString::generate(
&mut jwt_simple::reexports::rand::rngs::StdRng::from_entropy(),
);
let salt = SaltString::generate(OsRng);
Argon2::default()
.hash_password("testing_password".as_bytes(), &salt)
.unwrap()
Expand Down Expand Up @@ -246,7 +247,7 @@ pub mod test {
let password = dotenvy::var("SMTP_PASSWORD").unwrap();

let user_email = username.parse().unwrap();
let server_mailbox: Mailbox = format!("Developer DAO RPC <{}>", &username)
let server_mailbox: Mailbox = format!("Developer DAO RPC <{}>", username)
.parse()
.unwrap();

Expand Down
2 changes: 1 addition & 1 deletion src/routes/relayer/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ impl Relayer for PoktChains {
let provider = format!(
"https://api.cloud.developerdao.com/rpc/{}/{}",
self.id(),
&api_key
api_key
);
let byte_stream = PROXY_CLIENT
.post(provider)
Expand Down
8 changes: 4 additions & 4 deletions src/routes/token_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub async fn aggregate_balances(
chain,
PoktChains::Op | PoktChains::Bsc | PoktChains::Sui | PoktChains::Solana
) {
return Err(QueryError::ChainError)?;
Err(QueryError::ChainError)?
}

let endpoint: String = format!("https://api.cloud.developerdao.com/rpc/{chain}/{api_key}");
Expand Down Expand Up @@ -112,7 +112,7 @@ pub async fn aggregate_token_bals_for_user(
chain,
PoktChains::Op | PoktChains::Bsc | PoktChains::Sui | PoktChains::Solana
) {
return Err(QueryError::ChainError)?;
Err(QueryError::ChainError)?
}

let endpoint: String = format!("https://api.cloud.developerdao.com/rpc/{chain}/{api_key}");
Expand Down Expand Up @@ -144,7 +144,7 @@ pub async fn aggregate_single_token_bals(
chain,
PoktChains::Op | PoktChains::Bsc | PoktChains::Sui | PoktChains::Solana
) {
return Err(QueryError::ChainError)?;
Err(QueryError::ChainError)?
}

let endpoint: String = format!("https://api.cloud.developerdao.com/rpc/{chain}/{api_key}");
Expand Down Expand Up @@ -181,7 +181,7 @@ pub async fn get_batch_nft_info(
chain,
PoktChains::Op | PoktChains::Bsc | PoktChains::Sui | PoktChains::Solana
) {
return Err(QueryError::ChainError)?;
Err(QueryError::ChainError)?;
}

let endpoint: String = format!("https://api.cloud.developerdao.com/rpc/{chain}/{api_key}");
Expand Down
Loading