From 104b1a9bea829dcab4de6b9f57dbfb6040696139 Mon Sep 17 00:00:00 2001 From: Erik Reppel Date: Sun, 12 May 2024 18:12:53 -0700 Subject: [PATCH 1/4] Example of creating and submitting a premint in rust --- Cargo.toml | 2 +- examples/sign_and_submit.rs | 112 ++++++++++++++++++++++++++++++++++++ src/rules.rs | 3 +- 3 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 examples/sign_and_submit.rs diff --git a/Cargo.toml b/Cargo.toml index 47c65b2..ed77673 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,7 @@ futures-util = "0.3" futures-ticker = "0.0.3" mini-moka = "0.10.3" axum = "0.7.5" -reqwest = "0.12.3" +reqwest = { version = "0.12.3", features = ["json"] } url = "2.5.0" futures = "0.3.30" sha256 = "1.5.0" diff --git a/examples/sign_and_submit.rs b/examples/sign_and_submit.rs new file mode 100644 index 0000000..cafa013 --- /dev/null +++ b/examples/sign_and_submit.rs @@ -0,0 +1,112 @@ +use alloy::hex; +use alloy::primitives::U256; +use alloy::signers::wallet::LocalWallet; +use alloy::signers::Signer; +use mintpool::chain::view_contract_call; +use mintpool::chain_list::CHAINS; +use mintpool::config::Config; +use mintpool::premints::zora_premint::contract::IZoraPremintV2::{ + ContractCreationConfig, CreatorAttribution, TokenCreationConfig, +}; +use mintpool::premints::zora_premint::contract::{IZoraPremintV2, PREMINT_FACTORY_ADDR}; +use mintpool::premints::zora_premint::v2::V2; +use mintpool::rules::RulesEngine; +use mintpool::storage::PremintStorage; +use mintpool::types::PremintTypes; + +// Create a premint, sign it, and simulate submitting it +#[tokio::main] +async fn main() -> eyre::Result<()> { + let chain_id = 7777777; + + // end users wallet that signs the premint (doesn't need gas) + let user_wallet = LocalWallet::random(); + let user_address = user_wallet.address(); + + let chain = CHAINS.get_rpc(chain_id).await?; + let contract_config = ContractCreationConfig { + contractAdmin: user_address, + contractURI: "ipfs://someCollectionCid".to_string(), + contractName: "Example Zora premint mintpool message".to_string(), + }; + + // Get the collection address the premint would be deployed at on chain + let collection_address = { + let addr = view_contract_call( + IZoraPremintV2::getContractAddressCall { + contractConfig: contract_config.clone(), + }, + &chain, + PREMINT_FACTORY_ADDR, + ) + .await?; + addr._0 + }; + + let uid = 1; + + // Token creation settings. Importantly includes the token uri. + let token_creation_config = TokenCreationConfig { + tokenURI: "ipfs://tokenIPFSCid".to_string(), + maxSupply: U256::from(10000), + maxTokensPerAddress: 10, + pricePerToken: 0, + mintStart: Default::default(), + mintDuration: Default::default(), + royaltyBPS: 0, + payoutRecipient: user_address, + fixedPriceMinter: Default::default(), + createReferral: Default::default(), // this could be you! + }; + + // Creator & contract attributes + let creator_attribution = CreatorAttribution { + tokenConfig: token_creation_config, + uid, + version: 2, + deleted: false, + }; + + // Fully formed type + let mut premint = V2 { + collection_address, + chain_id, + collection: contract_config.clone(), + premint: creator_attribution.clone(), + signature: "".to_string(), + }; + + // compute and set the domain + premint.signature = { + let domain = premint.eip712_domain(); + let sig = user_wallet + .sign_typed_data::(&creator_attribution, &domain) + .await?; + hex::encode(sig.as_bytes()) + }; + + // Premint now should be valid + println!("Premint: {:?}", premint); + + let premint_item = PremintTypes::ZoraV2(premint); + + // optional: validate the premint against the default mintpool rules + let config = Config::test_default(); + let store = PremintStorage::new(&config).await; + let mut rules_engine = RulesEngine::new(&config); + rules_engine.add_default_rules(); + let result = rules_engine.evaluate(&premint_item, store).await?; + println!("Result: {:?}", result); + assert!(result.is_accept()); + + // Submit premint to a premint node + // can be any mintpool node + let client = reqwest::Client::new(); + client + .post("http://mintpool.zora.co/submit-premint") + .json(&premint_item) + .send() + .await?; + + Ok(()) +} diff --git a/src/rules.rs b/src/rules.rs index 9ac8386..4458f44 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -9,6 +9,7 @@ use serde::{Serialize, Serializer}; use crate::chain_list::{ChainListProvider, CHAINS}; use crate::config::Config; +use crate::storage::PremintStorage; use crate::storage::Reader; use crate::types::PremintTypes; @@ -279,7 +280,7 @@ macro_rules! typed_rule { }}; } -pub struct RulesEngine { +pub struct RulesEngine { rules: Vec>>, use_rpc: bool, } From 023f0bd4e2556a679020b766a13284fefbfe803e Mon Sep 17 00:00:00 2001 From: Erik Reppel Date: Mon, 13 May 2024 12:45:47 -0400 Subject: [PATCH 2/4] Fix test --- examples/sign_and_submit.rs | 2 ++ src/rules.rs | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/sign_and_submit.rs b/examples/sign_and_submit.rs index cafa013..5a20213 100644 --- a/examples/sign_and_submit.rs +++ b/examples/sign_and_submit.rs @@ -26,6 +26,7 @@ async fn main() -> eyre::Result<()> { let chain = CHAINS.get_rpc(chain_id).await?; let contract_config = ContractCreationConfig { contractAdmin: user_address, + // Uploading to IPFS should be a separate step contractURI: "ipfs://someCollectionCid".to_string(), contractName: "Example Zora premint mintpool message".to_string(), }; @@ -47,6 +48,7 @@ async fn main() -> eyre::Result<()> { // Token creation settings. Importantly includes the token uri. let token_creation_config = TokenCreationConfig { + // Uploading to IPFS should be a separate step tokenURI: "ipfs://tokenIPFSCid".to_string(), maxSupply: U256::from(10000), maxTokensPerAddress: 10, diff --git a/src/rules.rs b/src/rules.rs index 4458f44..d4751ca 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -172,9 +172,6 @@ impl RuleContext { } } -#[cfg(test)] -use crate::storage::PremintStorage; - #[cfg(test)] impl RuleContext { pub async fn test_default() -> Self { From e18d9f80f0970412db22509a58cd3160281617e4 Mon Sep 17 00:00:00 2001 From: Erik Reppel Date: Mon, 13 May 2024 12:49:42 -0400 Subject: [PATCH 3/4] Better comments --- examples/sign_and_submit.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/sign_and_submit.rs b/examples/sign_and_submit.rs index 5a20213..2fc3562 100644 --- a/examples/sign_and_submit.rs +++ b/examples/sign_and_submit.rs @@ -14,7 +14,9 @@ use mintpool::rules::RulesEngine; use mintpool::storage::PremintStorage; use mintpool::types::PremintTypes; -// Create a premint, sign it, and simulate submitting it +/// Create a premint, sign it, and simulate submitting it +/// This involves creating a Zora V2 premint type, then signing the `CreatorAttribution` struct +/// using typed messaged with the EIP-712 domain. #[tokio::main] async fn main() -> eyre::Result<()> { let chain_id = 7777777; @@ -31,7 +33,7 @@ async fn main() -> eyre::Result<()> { contractName: "Example Zora premint mintpool message".to_string(), }; - // Get the collection address the premint would be deployed at on chain + // Get the collection address the premint would be deployed at onchain let collection_address = { let addr = view_contract_call( IZoraPremintV2::getContractAddressCall { @@ -44,6 +46,8 @@ async fn main() -> eyre::Result<()> { addr._0 }; + // this can be any available uint, ideally monotonically increasing. + // If contract doesn't exist use 1, else use # tokens + 1 let uid = 1; // Token creation settings. Importantly includes the token uri. From 76e1cea09fc059eef925e578ef5e82df39c18c91 Mon Sep 17 00:00:00 2001 From: Erik Reppel Date: Mon, 13 May 2024 12:53:24 -0400 Subject: [PATCH 4/4] Update examples/sign_and_submit.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Bierlein --- examples/sign_and_submit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/sign_and_submit.rs b/examples/sign_and_submit.rs index 2fc3562..34e050c 100644 --- a/examples/sign_and_submit.rs +++ b/examples/sign_and_submit.rs @@ -109,7 +109,7 @@ async fn main() -> eyre::Result<()> { // can be any mintpool node let client = reqwest::Client::new(); client - .post("http://mintpool.zora.co/submit-premint") + .post("https://mintpool.zora.co/submit-premint") .json(&premint_item) .send() .await?;