use super::SqlxStore;
use super::entities::wallet_address::{self, WalletChainId};
use crate::model::{self, ActiveRow, SqlxModel};
use crate::pool::{Exec, SqlxTransaction};
use crate::schema::{AuthSchema, SqlxUserModel};
use crate::sql::Sql;
use crate::value::SqlxValue;
use alibi_core::error::{AuthError, AuthResult};
use alibi_core::store::WalletAddressStore;
use alibi_core::{AuthUser, CreateWalletAddress, WalletAddress};
use async_trait::async_trait;
use chrono::Utc;
use uuid::Uuid;
#[async_trait]
impl<S> WalletAddressStore for SqlxStore<S>
where
S: AuthSchema + Send + Sync,
S::User: SqlxUserModel,
{
async fn get_wallet_address(
&self,
address: &str,
chain_id: Option<f64>,
) -> AuthResult<Option<WalletAddress>> {
let mut sql = model::select_model::<wallet_address::Model>(self.exec());
sql.push(" WHERE ");
sql.compare(wallet_address::Model::TABLE, "address", " = ", address);
if let Some(chain_id) = chain_id {
sql.push(" AND ");
sql.compare(wallet_address::Model::TABLE, "chainId", " = ", chain_id);
}
model::limit_one(&mut sql);
Ok(self
.exec()
.fetch_optional::<wallet_address::Model>(sql)
.await?
.map(Into::into))
}
async fn create_wallet_address(&self, data: CreateWalletAddress) -> AuthResult<WalletAddress> {
if !data.chain_id.is_finite() {
return Err(AuthError::bad_request("Wallet chain ID must be finite"));
}
self.in_transaction(true, async move |tx| {
let exec = Exec::tx(tx);
let owner = super::users::find_user_by_id::<S::User>(
exec,
&data.user_id,
super::users::Lock::Shared,
)
.await?
.ok_or(AuthError::UserNotFound)?;
let mut active = ActiveRow::new();
active.set(
"id",
self.generated_id(self.exec(), "walletAddress", "walletAddress", "id")
.await?
.unwrap_or_else(|| Uuid::new_v4().to_string()),
);
active.set("userId", owner.id().into_owned());
active.set("address", data.address);
active.set(
"chainId",
WalletChainId::from(data.chain_id).into_sql_value(),
);
active.set("isPrimary", data.is_primary);
active.set("createdAt", Utc::now());
let created = model::insert::<wallet_address::Model>(exec, &active).await?;
Ok(created.into())
})
.await
}
}
pub(super) async fn remove_owned_wallets(tx: &SqlxTransaction, user_id: &str) -> AuthResult<()> {
let exec = Exec::tx(tx);
if !super::migrator::has_table(exec, "walletAddress").await? {
return Ok(());
}
let mut sql = Sql::with(exec.engine(), "DELETE FROM ");
sql.ident(wallet_address::Model::TABLE);
sql.push(" WHERE ");
sql.compare(wallet_address::Model::TABLE, "userId", " = ", user_id);
_ = exec.execute(sql).await?;
Ok(())
}