Skip to content

Commit

Permalink
feat: argent x account support
Browse files Browse the repository at this point in the history
Adds the ability to deploy and fetch Argent X accounts.
  • Loading branch information
xJonathanLEI committed Aug 13, 2023
1 parent 2cb377d commit abc8bf6
Show file tree
Hide file tree
Showing 8 changed files with 328 additions and 41 deletions.
46 changes: 40 additions & 6 deletions src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@ use starknet::{
core::{
serde::unsigned_field_element::UfeHex, types::FieldElement, utils::get_contract_address,
},
macros::felt,
macros::{felt, selector},
};

pub const KNOWN_ACCOUNT_CLASSES: [KnownAccountClass; 1] = [KnownAccountClass {
class_hash: felt!("0x048dd59fabc729a5db3afdf649ecaf388e931647ab2f53ca3c6183fa480aa292"),
variant: AccountVariantType::OpenZeppelin,
description: "OpenZeppelin account contract v0.6.1 compiled with cairo-lang v0.11.0.2",
}];
pub const KNOWN_ACCOUNT_CLASSES: [KnownAccountClass; 2] = [
KnownAccountClass {
class_hash: felt!("0x048dd59fabc729a5db3afdf649ecaf388e931647ab2f53ca3c6183fa480aa292"),
variant: AccountVariantType::OpenZeppelin,
description: "OpenZeppelin account contract v0.6.1 compiled with cairo-lang v0.11.0.2",
},
KnownAccountClass {
class_hash: felt!("0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918"),
variant: AccountVariantType::Argent,
description: "Argent X official proxy account",
},
];

#[derive(Serialize, Deserialize)]
pub struct AccountConfig {
Expand All @@ -27,6 +34,7 @@ pub struct AccountConfig {
#[serde(tag = "type", rename_all = "snake_case")]
pub enum AccountVariant {
OpenZeppelin(OzAccountConfig),
Argent(ArgentAccountConfig),
}

#[derive(Serialize, Deserialize)]
Expand All @@ -44,6 +52,7 @@ pub struct KnownAccountClass {

pub enum AccountVariantType {
OpenZeppelin,
Argent,
}

#[serde_as]
Expand All @@ -54,6 +63,18 @@ pub struct OzAccountConfig {
pub public_key: FieldElement,
}

#[serde_as]
#[derive(Serialize, Deserialize)]
pub struct ArgentAccountConfig {
pub version: u64,
#[serde_as(as = "UfeHex")]
pub implementation: FieldElement,
#[serde_as(as = "UfeHex")]
pub signer: FieldElement,
#[serde_as(as = "UfeHex")]
pub guardian: FieldElement,
}

#[serde_as]
#[derive(Serialize, Deserialize)]
pub struct UndeployedStatus {
Expand Down Expand Up @@ -88,6 +109,18 @@ impl AccountConfig {
&[oz.public_key],
FieldElement::ZERO,
)),
AccountVariant::Argent(argent) => Ok(get_contract_address(
undeployed_status.salt,
undeployed_status.class_hash,
&[
argent.implementation, // implementation
selector!("initialize"), // selector
FieldElement::TWO, // calldata_len
argent.signer, // calldata[0]: signer
argent.guardian, // calldata[1]: guardian
],
FieldElement::ZERO,
)),
}
}
}
Expand All @@ -96,6 +129,7 @@ impl Display for AccountVariantType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AccountVariantType::OpenZeppelin => write!(f, "OpenZeppelin"),
AccountVariantType::Argent => write!(f, "Argent X"),
}
}
}
63 changes: 63 additions & 0 deletions src/account_factory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use async_trait::async_trait;
use starknet::{
accounts::{
AccountFactory, ArgentAccountFactory, OpenZeppelinAccountFactory, RawAccountDeployment,
},
core::types::FieldElement,
providers::Provider,
signers::Signer,
};

pub enum AnyAccountFactory<S, P> {
OpenZeppelin(OpenZeppelinAccountFactory<S, P>),
Argent(ArgentAccountFactory<S, P>),
}

#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl<S, P> AccountFactory for AnyAccountFactory<S, P>
where
S: Signer + Sync + Send,
P: Provider + Sync + Send,
{
type Provider = P;
type SignError = S::SignError;

fn class_hash(&self) -> FieldElement {
match self {
AnyAccountFactory::OpenZeppelin(inner) => inner.class_hash(),
AnyAccountFactory::Argent(inner) => inner.class_hash(),
}
}

fn calldata(&self) -> Vec<FieldElement> {
match self {
AnyAccountFactory::OpenZeppelin(inner) => inner.calldata(),
AnyAccountFactory::Argent(inner) => inner.calldata(),
}
}

fn chain_id(&self) -> FieldElement {
match self {
AnyAccountFactory::OpenZeppelin(inner) => inner.chain_id(),
AnyAccountFactory::Argent(inner) => inner.chain_id(),
}
}

fn provider(&self) -> &Self::Provider {
match self {
AnyAccountFactory::OpenZeppelin(inner) => inner.provider(),
AnyAccountFactory::Argent(inner) => inner.provider(),
}
}

async fn sign_deployment(
&self,
deployment: &RawAccountDeployment,
) -> Result<Vec<FieldElement>, Self::SignError> {
match self {
AnyAccountFactory::OpenZeppelin(inner) => inner.sign_deployment(deployment).await,
AnyAccountFactory::Argent(inner) => inner.sign_deployment(deployment).await,
}
}
}
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use colored::Colorize;
use crate::{provider::ProviderArgs, subcommands::*};

mod account;
mod account_factory;
mod address_book;
mod casm;
mod chain_id;
Expand Down
92 changes: 92 additions & 0 deletions src/subcommands/account/argent/init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use std::{io::Write, path::PathBuf};

use anyhow::Result;
use clap::Parser;
use colored::Colorize;
use starknet::{
core::types::FieldElement,
macros::felt,
signers::{Signer, SigningKey},
};

use crate::{
account::{
AccountConfig, AccountVariant, ArgentAccountConfig, DeploymentStatus, UndeployedStatus,
},
path::ExpandedPathbufParser,
signer::SignerArgs,
};

/// Official hashes used as of extension version 5.7.0
const ARGENT_PROXY_CLASS_HASH: FieldElement =
felt!("0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918");
const ARGENT_IMPL_CLASS_HASH: FieldElement =
felt!("0x033434ad846cdd5f23eb73ff09fe6fddd568284a0fb7d1be20ee482f044dabe2");

#[derive(Debug, Parser)]
pub struct Init {
// TODO: allow manually specifying public key without using a signer
#[clap(flatten)]
signer: SignerArgs,
#[clap(
long,
short,
help = "Overwrite the account config file if it already exists"
)]
force: bool,
#[clap(
value_parser = ExpandedPathbufParser,
help = "Path to save the account config file"
)]
output: PathBuf,
}

impl Init {
pub async fn run(self) -> Result<()> {
if self.output.exists() && !self.force {
anyhow::bail!("account config file already exists");
}

let signer = self.signer.into_signer()?;

// Too lazy to write random salt generation
let salt = SigningKey::from_random().secret_scalar();

let account_config = AccountConfig {
version: 1,
variant: AccountVariant::Argent(ArgentAccountConfig {
version: 1,
implementation: ARGENT_IMPL_CLASS_HASH,
signer: signer.get_public_key().await?.scalar(),
guardian: FieldElement::ZERO,
}),
deployment: DeploymentStatus::Undeployed(UndeployedStatus {
class_hash: ARGENT_PROXY_CLASS_HASH,
salt,
}),
};

let deployed_address = account_config.deploy_account_address()?;

let mut file = std::fs::File::create(&self.output)?;
serde_json::to_writer_pretty(&mut file, &account_config)?;
file.write_all(b"\n")?;

eprintln!(
"Created new account config file: {}",
std::fs::canonicalize(&self.output)?.display()
);
eprintln!();
eprintln!(
"Once deployed, this account will be available at:\n {}",
format!("{:#064x}", deployed_address).bright_yellow()
);
eprintln!();
eprintln!(
"Deploy this account by running:\n {}",
format!("starkli account deploy {}", self.output.display()).bright_yellow()
);

Ok(())
}
}
25 changes: 25 additions & 0 deletions src/subcommands/account/argent/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use anyhow::Result;
use clap::{Parser, Subcommand};

mod init;
use init::Init;

#[derive(Debug, Parser)]
pub struct Argent {
#[clap(subcommand)]
command: Subcommands,
}

#[derive(Debug, Subcommand)]
enum Subcommands {
#[clap(about = "Create a new account configuration without actually deploying")]
Init(Init),
}

impl Argent {
pub async fn run(self) -> Result<()> {
match self.command {
Subcommands::Init(cmd) => cmd.run().await,
}
}
}
69 changes: 47 additions & 22 deletions src/subcommands/account/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ use anyhow::Result;
use clap::Parser;
use colored::Colorize;
use starknet::{
accounts::{AccountFactory, OpenZeppelinAccountFactory},
accounts::{AccountFactory, ArgentAccountFactory, OpenZeppelinAccountFactory},
core::types::FieldElement,
providers::Provider,
signers::Signer,
};

use crate::{
account::{AccountConfig, AccountVariant, DeployedStatus, DeploymentStatus},
account_factory::AnyAccountFactory,
fee::{FeeArgs, FeeSetting},
path::ExpandedPathbufParser,
signer::SignerArgs,
Expand Down Expand Up @@ -63,10 +64,7 @@ impl Deploy {
let mut account: AccountConfig =
serde_json::from_reader(&mut std::fs::File::open(&self.file)?)?;

#[allow(clippy::infallible_destructuring_match)]
let oz_config = match &account.variant {
AccountVariant::OpenZeppelin(inner) => inner,
};
let signer_public_key = signer.get_public_key().await?.scalar();

let undeployed_status = match &account.deployment {
DeploymentStatus::Undeployed(inner) => inner,
Expand All @@ -75,25 +73,52 @@ impl Deploy {
}
};

// Makes sure we're using the right key
let signer_public_key = signer.get_public_key().await?.scalar();
if signer_public_key != oz_config.public_key {
anyhow::bail!(
"public key mismatch. Expected: {:#064x}; actual: {:#064x}.",
oz_config.public_key,
signer_public_key
);
}

let chain_id = provider.chain_id().await?;

let factory = OpenZeppelinAccountFactory::new(
undeployed_status.class_hash,
chain_id,
signer.clone(),
provider.clone(),
)
.await?;
let factory = match &account.variant {
AccountVariant::OpenZeppelin(oz_config) => {
// Makes sure we're using the right key
if signer_public_key != oz_config.public_key {
anyhow::bail!(
"public key mismatch. Expected: {:#064x}; actual: {:#064x}.",
oz_config.public_key,
signer_public_key
);
}

AnyAccountFactory::OpenZeppelin(
OpenZeppelinAccountFactory::new(
undeployed_status.class_hash,
chain_id,
signer.clone(),
provider.clone(),
)
.await?,
)
}
AccountVariant::Argent(argent_config) => {
// Makes sure we're using the right key
if signer_public_key != argent_config.signer {
anyhow::bail!(
"public key mismatch. Expected: {:#064x}; actual: {:#064x}.",
argent_config.signer,
signer_public_key
);
}

AnyAccountFactory::Argent(
ArgentAccountFactory::new(
undeployed_status.class_hash,
argent_config.implementation,
chain_id,
FieldElement::ZERO,
signer.clone(),
provider.clone(),
)
.await?,
)
}
};

let account_deployment = factory.deploy(undeployed_status.salt);

Expand Down
Loading

0 comments on commit abc8bf6

Please sign in to comment.