use super::client::NodeClient;
use super::sequencer::Nonce;
use crate::indexer::{Address, Subaccount};
use anyhow::{anyhow as err, Error};
use bip32::{DerivationPath, Language, Mnemonic, Seed};
use cosmrs::{
crypto::{secp256k1::SigningKey, PublicKey},
tx, AccountId,
};
use delegate::delegate;
use std::{
collections::HashMap,
hash::{Hash, Hasher},
str::FromStr,
};
const BECH32_PREFIX_DYDX: &str = "dydx";
pub struct Wallet {
seed: Seed,
}
impl Wallet {
pub fn from_mnemonic(mnemonic: &str) -> Result<Self, Error> {
let seed = Mnemonic::new(mnemonic, Language::English)?.to_seed("");
Ok(Self { seed })
}
pub async fn account(&self, index: u32, client: &mut NodeClient) -> Result<Account, Error> {
let mut account = self.account_offline(index)?;
(
account.public.account_number,
account.public.sequence_number,
) = client.query_address(account.address()).await?;
Ok(account)
}
pub fn account_offline(&self, index: u32) -> Result<Account, Error> {
self.derive_account(index, BECH32_PREFIX_DYDX)
}
#[cfg(feature = "noble")]
pub fn noble(&self) -> noble::WalletOps<'_> {
noble::WalletOps::new(self)
}
fn derive_account(&self, index: u32, prefix: &str) -> Result<Account, Error> {
let derivation_str = format!("m/44'/118'/0'/0/{index}");
let derivation_path = DerivationPath::from_str(&derivation_str)?;
let private_key = SigningKey::derive_from_path(&self.seed, &derivation_path)?;
let public_key = private_key.public_key();
let account_id = public_key.account_id(prefix).map_err(Error::msg)?;
let address = account_id.to_string().parse()?;
Ok(Account {
public: PublicAccount {
address,
account_number: 0,
sequence_number: 0,
next_nonce: None,
},
account_id,
index,
key: private_key,
auths: AuthenticatorsManager::default(),
})
}
}
pub struct Account {
index: u32,
key: SigningKey,
#[allow(dead_code)]
account_id: AccountId,
auths: AuthenticatorsManager,
public: PublicAccount,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PublicAccount {
address: Address,
account_number: u64,
sequence_number: u64,
next_nonce: Option<Nonce>,
}
#[derive(Clone, Debug, Default)]
pub struct AuthenticatorsManager {
auths: HashMap<Address, (PublicAccount, Vec<u64>)>,
}
impl Account {
pub fn index(&self) -> &u32 {
&self.index
}
pub fn public_key(&self) -> PublicKey {
self.key.public_key()
}
pub fn sign(&self, doc: tx::SignDoc) -> Result<tx::Raw, Error> {
doc.sign(&self.key)
.map_err(|e| err!("Failure to sign doc: {e}"))
}
pub fn authenticators(&self) -> &AuthenticatorsManager {
&self.auths
}
pub fn authenticators_mut(&mut self) -> &mut AuthenticatorsManager {
&mut self.auths
}
delegate! {
to self.public {
pub fn address(&self) -> &Address;
pub fn subaccount(&self, number: u32) -> Result<Subaccount, Error>;
pub fn account_number(&self) -> u64;
pub fn sequence_number(&self) -> u64;
pub fn set_sequence_number(&mut self, sequence_number: u64);
pub fn next_nonce(&self) -> Option<&Nonce>;
pub fn set_next_nonce(&mut self, nonce: Nonce);
}
}
}
impl PublicAccount {
pub async fn updated(address: Address, client: &mut NodeClient) -> Result<Self, Error> {
let mut account = PublicAccount::new(address);
account.update(client).await?;
Ok(account)
}
pub fn new(address: Address) -> Self {
Self {
address,
account_number: 0,
sequence_number: 0,
next_nonce: None,
}
}
pub async fn update(&mut self, client: &mut NodeClient) -> Result<(), Error> {
(self.account_number, self.sequence_number) = client.query_address(self.address()).await?;
Ok(())
}
pub fn address(&self) -> &Address {
&self.address
}
pub fn subaccount(&self, number: u32) -> Result<Subaccount, Error> {
Ok(Subaccount::new(self.address.clone(), number.try_into()?))
}
pub fn account_number(&self) -> u64 {
self.account_number
}
pub fn sequence_number(&self) -> u64 {
self.sequence_number
}
pub fn set_sequence_number(&mut self, sequence_number: u64) {
self.sequence_number = sequence_number;
}
pub fn next_nonce(&self) -> Option<&Nonce> {
self.next_nonce.as_ref()
}
pub fn set_next_nonce(&mut self, nonce: Nonce) {
if let Nonce::Sequence(number) = nonce {
self.sequence_number = number
}
self.next_nonce = Some(nonce);
}
}
impl AuthenticatorsManager {
pub fn get(&self, authing: &Address) -> Option<&(PublicAccount, Vec<u64>)> {
self.auths.get(authing)
}
pub fn get_mut(&mut self, authing: &Address) -> Option<&mut (PublicAccount, Vec<u64>)> {
self.auths.get_mut(authing)
}
pub fn add(&mut self, auth: PublicAccount, id: u64) {
if let Some(ids) = self.auths.get_mut(auth.address()) {
ids.1.push(id);
} else {
self.auths.insert(auth.address().clone(), (auth, vec![id]));
}
}
pub fn remove(&mut self, authing: &Address) -> Option<(PublicAccount, Vec<u64>)> {
self.auths.remove(authing)
}
}
impl Hash for PublicAccount {
fn hash<H: Hasher>(&self, state: &mut H) {
self.address.hash(state);
}
}
impl From<Address> for PublicAccount {
fn from(address: Address) -> Self {
PublicAccount::new(address)
}
}
#[cfg(feature = "noble")]
mod noble {
use super::*;
use crate::noble::NobleClient;
const BECH32_PREFIX_NOBLE: &str = "noble";
pub struct WalletOps<'w> {
wallet: &'w Wallet,
}
impl<'w> WalletOps<'w> {
pub fn new(wallet: &'w Wallet) -> Self {
Self { wallet }
}
pub async fn account(
&self,
index: u32,
client: &mut NobleClient,
) -> Result<Account, Error> {
let mut account = self.account_offline(index)?;
(
account.public.account_number,
account.public.sequence_number,
) = client.query_address(account.address()).await?;
Ok(account)
}
pub fn account_offline(&self, index: u32) -> Result<Account, Error> {
self.wallet.derive_account(index, BECH32_PREFIX_NOBLE)
}
}
}