diff --git a/Cargo.lock b/Cargo.lock index 63b86948..3e3ff5ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1779,6 +1779,7 @@ dependencies = [ "mpl-core 0.8.0", "mpl-core-candy-guard", "mpl-core-candy-machine-core", + "mpl-hybrid", "mpl-token-auth-rules", "mpl-token-metadata 4.1.2", "once_cell", @@ -4401,6 +4402,23 @@ dependencies = [ "spl-token 4.0.0", ] +[[package]] +name = "mpl-hybrid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4382c23124be4c928da30380464f7910e1cc4b68f84d211532c5beb46403c51" +dependencies = [ + "base64 0.22.1", + "borsh 0.10.4", + "modular-bitfield", + "num-derive 0.3.3", + "num-traits", + "rmp-serde", + "serde_json", + "solana-program 1.16.27", + "thiserror", +] + [[package]] name = "mpl-token-auth-rules" version = "1.5.1" diff --git a/crates/cmds-solana/Cargo.toml b/crates/cmds-solana/Cargo.toml index 6a0b12b7..e57e00b9 100644 --- a/crates/cmds-solana/Cargo.toml +++ b/crates/cmds-solana/Cargo.toml @@ -79,6 +79,7 @@ pyth-sdk-solana = { git = "https://github.com/space-operator/pyth-sdk-rs", rev = # anchor anchor-lang = "=0.28.0" anchor-spl = "=0.28.0" +mpl-hybrid = "0.1.0" # clockwork-client = "=2.0.1" # clockwork-utils = "=2.0.1" diff --git a/crates/cmds-solana/node-definitions/mpl_hybrid/mpl_hybrid.json b/crates/cmds-solana/node-definitions/mpl_hybrid/mpl_hybrid.json new file mode 100644 index 00000000..cd9b2e92 --- /dev/null +++ b/crates/cmds-solana/node-definitions/mpl_hybrid/mpl_hybrid.json @@ -0,0 +1,240 @@ +{ + "type": "native", + "data": { + "node_definition_version": "0.1", + "unique_id": "", + "node_id": "mpl_hybrid", + "version": "0.1", + "display_name": "MplHybrid", + "description": "Mpl hybrid impl", + "tags": [], + "related_to": [ + { + "id": "", + "type": "", + "relationship": "" + } + ], + "resources": { + "source_code_url": "", + "documentation_url": "" + }, + "usage": { + "license": "Apache-2.0", + "license_url": "", + "pricing": { + "currency": "USDC", + "purchase_price": 0, + "price_per_run": 0, + "custom": { + "unit": "monthly", + "value": "0" + } + } + }, + "authors": [ + { + "name": "Space Operator", + "contact": "" + } + ], + "design": null, + "options": null, + "instruction_info": { + "before": [], + "signature": "signature", + "after": [] + } + }, + "sources": [ + { + "name": "signature", + "type": "signature", + "defaultValue": null, + "tooltip": "", + "optional": true + } + ], + "targets": [ + { + "name": "fee_payer", + "type_bounds": ["keypair"], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": true + }, + { + "name": "owner", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "authority", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "escrow", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "asset", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "collection", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "user_token_account", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "escrow_token_account", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "token", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "fee_token_account", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "fee_sol_account", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "fee_project_account", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "recent_blockhashes", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "mpl_core", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "system_program", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "token_program", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "associated_token_program", + "type_bounds": [ + "pubkey" + ], + "required": true, + "defaultValue": null, + "tooltip": "", + "passthrough": false + }, + { + "name": "submit", + "type_bounds": [ + "bool" + ], + "required": false, + "defaultValue": true, + "tooltip": "", + "passthrough": false + } + ], + "targets_form.ui_schema": {}, + "targets_form.json_schema": {} +} \ No newline at end of file diff --git a/crates/cmds-solana/src/mpl_hybrid/mpl_hybrid.rs b/crates/cmds-solana/src/mpl_hybrid/mpl_hybrid.rs new file mode 100644 index 00000000..d6a58561 --- /dev/null +++ b/crates/cmds-solana/src/mpl_hybrid/mpl_hybrid.rs @@ -0,0 +1,102 @@ +use flow_lib::command::prelude::*; +const NAME: &str = "mpl_hybrid"; +flow_lib::submit!(CommandDescription::new(NAME, | _ | build())); +fn build() -> BuildResult { + const DEFINITION: &str = flow_lib::node_definition!("mpl_hybrid/mpl_hybrid.json"); + static CACHE: BuilderCache = BuilderCache::new(|| { + CmdBuilder::new(DEFINITION)?.check_name(NAME) + }); + Ok(CACHE.clone()?.build(run)) +} +#[serde_as] +#[derive(Deserialize, Serialize, Debug)] +pub struct Input { + #[serde(with = "value::keypair")] + pub fee_payer: Keypair, + #[serde_as(as = "AsPubkey")] + owner: Pubkey, + #[serde_as(as = "AsPubkey")] + authority: Pubkey, + #[serde_as(as = "AsPubkey")] + escrow: Pubkey, + #[serde_as(as = "AsPubkey")] + asset: Pubkey, + #[serde_as(as = "AsPubkey")] + collection: Pubkey, + #[serde_as(as = "AsPubkey")] + user_token_account: Pubkey, + #[serde_as(as = "AsPubkey")] + escrow_token_account: Pubkey, + #[serde_as(as = "AsPubkey")] + token: Pubkey, + #[serde_as(as = "AsPubkey")] + fee_token_account: Pubkey, + #[serde_as(as = "AsPubkey")] + fee_sol_account: Pubkey, + #[serde_as(as = "AsPubkey")] + fee_project_account: Pubkey, + #[serde_as(as = "AsPubkey")] + recent_blockhashes: Pubkey, + #[serde_as(as = "AsPubkey")] + mpl_core: Pubkey, + #[serde_as(as = "AsPubkey")] + system_program: Pubkey, + #[serde_as(as = "AsPubkey")] + token_program: Pubkey, + #[serde_as(as = "AsPubkey")] + associated_token_program: Pubkey, + #[serde(default = "value::default::bool_true")] + submit: bool, +} +#[serde_as] +#[derive(Deserialize, Serialize, Debug)] +pub struct Output { + #[serde_as(as = "Option")] + pub signature: Option, +} +async fn run(mut ctx: Context, input: Input) -> Result { + tracing::info!("input: {:?}", input); + let recent_blockhash = client.get_latest_blockhash().await?; + let blockhash_as_str = recent_blockhash.clone().to_string(); + let capture_instraction = mpl_hybrid::instructions::CaptureV1{ + input.owner, + input.authority, + input.escrow, + input.asset, + input.collection, + input.user_token_account, + input.escrow_token_account, + input.token, + input.fee_token_account, + input.fee_sol_account, + input.fee_project_account, + input.fee_token_account, + input.recent_blockhashes, + input.mpl_core, + input.system_program, + input.token_program, + input.associated_token_program, + }.instruction_with_remaining_accounts(&[]); + + let instructions = Instructions { + fee_payer: input.fee_payer.pubkey(), + signers: [input.fee_payer.clone_keypair()].into(), + instructions: [capture_instraction].into(), + }; + let signature = ctx.execute(instructions, <_>::default()).await?.signature; + + Ok(Output { signature }) +} +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_build() { + build().unwrap(); + } + #[tokio::test] + async fn test_run() { + let ctx = Context::default(); + build().unwrap().run(ctx, ValueSet::new()).await.unwrap_err(); + } +} diff --git a/crates/cmds-solana/src/utils.rs b/crates/cmds-solana/src/utils.rs index d5c9cb41..b525ca43 100644 --- a/crates/cmds-solana/src/utils.rs +++ b/crates/cmds-solana/src/utils.rs @@ -23,7 +23,7 @@ pub async fn execute( instructions: &[Instruction], ) -> crate::Result<(Transaction, Hash)> { let recent_blockhash = client.get_latest_blockhash().await?; - + let message = Message::new_with_blockhash(instructions, Some(fee_payer), &recent_blockhash); let transaction = Transaction::new_unsigned(message);