Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ERC20 Premints #48

Merged
merged 10 commits into from
May 3, 2024
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ env:

jobs:
build_and_test:
runs-on: rust_oss_runner
runs-on: ubuntu-latest
environment: Testing

steps:
Expand Down
1 change: 0 additions & 1 deletion src/chain.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::chain_list::{ChainListProvider, CHAINS};
use crate::controller::{ControllerCommands, ControllerInterface};
use crate::premints::zora_premint_v2::types::PREMINT_FACTORY_ADDR;
use crate::types::{InclusionClaim, Premint, PremintTypes};
use alloy::primitives::{address, Address, Bytes, TxKind};
use alloy::providers::Provider;
Expand Down
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clap::Parser;
use mintpool::api;
use mintpool::metrics::init_metrics_and_logging;
use mintpool::premints::zora_premint_v2::types::ZoraPremintV2;
use mintpool::premints::zora_premint::v2::V2;
use mintpool::rules::RulesEngine;
use mintpool::run::{start_p2p_services, start_watch_chain};
use mintpool::stdin::watch_stdin;
Expand All @@ -23,7 +23,7 @@ async fn main() -> eyre::Result<()> {
let router = api::router_with_defaults(&config).merge(metrics_router);
api::start_api(&config, ctl.clone(), router, true).await?;

start_watch_chain::<ZoraPremintV2>(&config, ctl.clone()).await;
start_watch_chain::<V2>(&config, ctl.clone()).await;
tracing::info!(monotonic_counter.chains_watched = 1, "Watching chain");
if config.interactive {
watch_stdin(ctl.clone()).await;
Expand Down
2 changes: 1 addition & 1 deletion src/premints/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub mod metadata;
pub mod zora_premint_v2;
pub mod zora_premint;
20 changes: 20 additions & 0 deletions src/premints/zora_premint/contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use alloy::primitives::{address, Address};
use alloy::sol;
use serde::{Deserialize, Serialize};

pub static PREMINT_FACTORY_ADDR: Address = address!("7777773606e7e46C8Ba8B98C08f5cD218e31d340");

// we need to use separate namespaces for each premint version,
// because they all need to have the type names for the signatures
// to calculate correctly
sol! {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
IZoraPremintERC20V1,
"src/premints/zora_premint/zora1155PremintExecutor_erc20_1.json"
}

sol! {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
IZoraPremintV2,
"src/premints/zora_premint/zora1155PremintExecutor_v2.json"
}
70 changes: 70 additions & 0 deletions src/premints/zora_premint/erc20v1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use crate::{implement_zora_premint_traits, typed_rule};
use alloy::primitives::Address;

use crate::premints::zora_premint::contract::IZoraPremintERC20V1;
use crate::premints::zora_premint::contract::IZoraPremintV2::IZoraPremintV2Errors;

use crate::rules::Rule;
use crate::storage::Reader;
use crate::types::PremintTypes;

impl Default for IZoraPremintERC20V1::ContractCreationConfig {
fn default() -> Self {
Self {
contractAdmin: Default::default(),
contractURI: Default::default(),
contractName: Default::default(),
}
}
}

impl Default for IZoraPremintERC20V1::TokenCreationConfig {
fn default() -> Self {
Self {
tokenURI: Default::default(),
maxSupply: Default::default(),
maxTokensPerAddress: Default::default(),
pricePerToken: Default::default(),
mintStart: Default::default(),
mintDuration: Default::default(),
royaltyBPS: Default::default(),
fixedPriceMinter: Default::default(),
royaltyMintSchedule: Default::default(),
royaltyRecipient: Default::default(),
}
}
}

impl Default for IZoraPremintERC20V1::CreatorAttribution {
fn default() -> Self {
Self {
tokenConfig: Default::default(),
uid: Default::default(),
version: Default::default(),
deleted: Default::default(),
}
}
}

implement_zora_premint_traits!(
IZoraPremintERC20V1,
ERC20V1,
"zora_premint_erc20v1",
"ERC20_1"
);

pub fn all_v2_rules<T: Reader>() -> Vec<Box<dyn Rule<T>>> {
vec![
typed_rule!(
PremintTypes::ZoraERC20V1,
ERC20V1::is_authorized_to_create_premint
),
typed_rule!(PremintTypes::ZoraERC20V1, ERC20V1::is_valid_signature),
typed_rule!(PremintTypes::ZoraERC20V1, ERC20V1::is_chain_supported),
typed_rule!(PremintTypes::ZoraERC20V1, ERC20V1::not_minted),
typed_rule!(
PremintTypes::ZoraERC20V1,
ERC20V1::premint_version_supported
),
]
}
5 changes: 5 additions & 0 deletions src/premints/zora_premint/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod contract;
pub mod erc20v1;
pub mod rules;
pub mod types;
pub mod v2;
137 changes: 137 additions & 0 deletions src/premints/zora_premint/rules.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use crate::premints::zora_premint::v2::V2;
use crate::rules::Rule;
use crate::storage::Reader;
use crate::typed_rule;
use crate::types::PremintTypes;
#[macro_export]
macro_rules! zora_premint_rules {
($namespace:tt, $typ:ty, $version:literal) => {
pub async fn is_authorized_to_create_premint<T: $crate::storage::Reader>(
premint: &$typ,
context: &$crate::rules::RuleContext<T>,
) -> eyre::Result<$crate::rules::Evaluation> {
let rpc = match context.rpc {
None => return $crate::ignore!("Rule requires RPC call"),
Some(ref rpc) => rpc,
};

let call = $namespace::isAuthorizedToCreatePremintCall {
contractAddress: premint.collection_address,
signer: premint.collection.contractAdmin,
premintContractConfigContractAdmin: premint.collection.contractAdmin,
};

let result = $crate::chain::view_contract_call(
call,
rpc,
$crate::premints::zora_premint::contract::PREMINT_FACTORY_ADDR,
)
.await?;

match result.isAuthorized {
true => Ok($crate::rules::Evaluation::Accept),
false => $crate::reject!("Unauthorized to create premint"),
}
}

pub async fn not_minted<T: $crate::storage::Reader>(
premint: &$typ,
context: &$crate::rules::RuleContext<T>,
) -> eyre::Result<$crate::rules::Evaluation> {
let rpc = match context.rpc {
None => return $crate::ignore!("Rule requires RPC provider"),
Some(ref rpc) => rpc,
};

let call = $namespace::premintStatusCall {
contractAddress: premint.collection_address,
uid: premint.premint.uid,
};

let result = $crate::chain::view_contract_call(
call,
rpc,
$crate::premints::zora_premint::contract::PREMINT_FACTORY_ADDR,
)
.await?;

match result.contractCreated && !result.tokenIdForPremint.is_zero() {
false => Ok($crate::rules::Evaluation::Accept),
true => $crate::reject!("Premint already minted"),
}
}

pub async fn premint_version_supported<T: $crate::storage::Reader>(
premint: &$typ,
context: &$crate::rules::RuleContext<T>,
) -> eyre::Result<$crate::rules::Evaluation> {
let rpc = match context.rpc {
None => return $crate::ignore!("Rule requires RPC provider"),
Some(ref rpc) => rpc,
};

let call = $namespace::supportedPremintSignatureVersionsCall {
contractAddress: premint.collection_address,
};

let result = $crate::chain::view_contract_call(
call,
rpc,
$crate::premints::zora_premint::contract::PREMINT_FACTORY_ADDR,
)
.await?;

match result.versions.contains(&$version.to_string()) {
true => Ok($crate::rules::Evaluation::Accept),
false => $crate::reject!(concat!(
"Premint version ",
$version,
" not supported by contract"
)),
}
}

// * signatureIsValid ( this can be performed entirely offline )
// * check if the signature is valid
// * check if the signature is equal to the proposed contract admin

pub async fn is_valid_signature<T: $crate::storage::Reader>(
premint: &$typ,
_context: &$crate::rules::RuleContext<T>,
) -> eyre::Result<$crate::rules::Evaluation> {
// * if contract exists, check if the signer is the contract admin
// * if contract does not exist, check if the signer is the proposed contract admin

let signature: alloy::signers::Signature =
core::str::FromStr::from_str(premint.signature.as_str())?;

let domain = premint.eip712_domain();
let hash = alloy::sol_types::SolStruct::eip712_signing_hash(&premint.premint, &domain);
let signer: alloy::primitives::Address =
signature.recover_address_from_prehash(&hash)?;

if signer != premint.collection.contractAdmin {
$crate::reject!(
"Invalid signature for contract admin {} vs recovered {}",
premint.collection.contractAdmin,
signer
)
} else {
Ok($crate::rules::Evaluation::Accept)
}
}

pub async fn is_chain_supported<T: $crate::storage::Reader>(
premint: &$typ,
_context: &$crate::rules::RuleContext<T>,
) -> eyre::Result<$crate::rules::Evaluation> {
let supported_chains: Vec<u64> = vec![7777777, 999999999, 8453];
let chain_id = premint.chain_id;

match supported_chains.contains(&chain_id) {
true => Ok($crate::rules::Evaluation::Accept),
false => $crate::reject!("Chain not supported"),
}
}
};
}
Loading
Loading