Space Operator
  • Welcome
  • Visual Builder
    • Flows
      • API Key & POST Request
      • WebSocket
        • Signature Requests
      • Nested Flows
      • Learn Solana
      • Add flows to your websites
      • Iterate with Localhost
    • Nodes
      • Web Assembly Nodes
        • space-cli
        • space-lib
        • Examples in Rust
          • Rectangle
          • Filter
          • Regex
        • Uploading WASM binary via UI
      • Native Nodes
        • Code Template
        • Node Definition
        • ValueSet
        • Submitting Native Nodes
        • Tracing
      • Mock Nodes
      • JS Node
      • API Input Node
  • Self-hosting
    • Docker Compose
    • Serving HTTPS
    • Lightsail Instance
    • Export data to your instance
  • FAQ
    • Servers
  • References
    • Flows
    • Flow Deployment
Powered by GitBook
On this page
  • Steps
  • Example

Was this helpful?

  1. Visual Builder
  2. Nodes
  3. Native Nodes

Code Template

Steps

1. Implement CommandTrait :

  1. fn name() -> Name : return the name of the command

  2. fn inputs() -> Vec<CmdInputDescription> : return an array of input description, fields are:

    1. name: name of the input

    2. type_bounds : allowed types

    3. required

    4. passthrough : if true, this field will be present in the output

  3. fn outputs() -> Vec<CmdOutputDescription>:

    1. name: name of the output

    2. type: type of the output

  4. async fn run(&self, ctx: Arc<Context>, inputs: ValueSet) -> Result<ValueSet, Error> {}

2. Submit with inventory::submit

3. To get inputs, define a struct that implement Deserialize:

#[derive(Serialize, Deserialize, Debug)]
pub struct Input {
    // special types like Keypair, Pubkey, Signature, Decimal
    // have to be decorated with `#[serde(with = "...")]`
    #[serde(with = "value::keypair")]
    pub sender: Keypair,
    #[serde(with = "value::pubkey")]
    pub recipient: Pubkey,
    // use default and ::opt for optional value
    #[serde(default, with = "value::pubkey::opt")]
    pub opt_pubkey: Option<Pubkey>,
    #[serde(with = "value::decimal")]
    pub amount: Decimal,
    // optional input with a default value,
    // use `#[serde(default = ...)]`
    #[serde(default = "value::default::bool_true")]
    pub submit: bool,
}
 let input: Input = value::from_map(inputs)?;

4. Output is the same, but derive Serialize on it and use value::to_map.

Ok(value::to_map(&Output {
  pubkey: keypair.pubkey(),
  keypair,
})?)

Example

// assuming `crate` is `cmds-solana`
use crate::prelude::*;

#[derive(Debug, Clone)]
pub struct TransferSol;

#[derive(Serialize, Deserialize, Debug)]
pub struct Input {
    #[serde(with = "value::keypair")]
    pub sender: Keypair,
    #[serde(with = "value::pubkey")]
    pub recipient: Pubkey,
    #[serde(with = "value::decimal")]
    pub amount: Decimal,
    // optional `submit` input, `true` is not present
    #[serde(default = "value::default::bool_true")]
    pub submit: bool,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Output {
    // optional `signature` output, needs `::opt`
    #[serde(with = "value::signature::opt")]
    pub signature: Option<Signature>,
    pub tx: String,
}

const SOLANA_TRANSFER_SOL: &str = "transfer_sol";

// Inputs
const SENDER: &str = "sender";
const RECIPIENT: &str = "recipient";
const AMOUNT: &str = "amount";
const SUBMIT: &str = "submit";

// Outputs
const TX: &str = "tx";
const SIGNATURE: &str = "signature";

#[async_trait]
impl CommandTrait for TransferSol {
    fn name(&self) -> Name {
        SOLANA_TRANSFER_SOL.into()
    }

    fn inputs(&self) -> Vec<CmdInput> {
        [
            CmdInput {
                name: SENDER.into(),
                type_bounds: [ValueType::Keypair, ValueType::String].to_vec(),
                required: true,
                passthrough: false,
            },
            CmdInput {
                name: RECIPIENT.into(),
                type_bounds: [ValueType::Pubkey, ValueType::Keypair, ValueType::String].to_vec(),
                required: true,
                passthrough: false,
            },
            CmdInput {
                name: AMOUNT.into(),
                type_bounds: [ValueType::F64].to_vec(),
                required: true,
                passthrough: false,
            },
            CmdInput {
                name: SUBMIT.into(),
                type_bounds: [ValueType::Bool].to_vec(),
                required: false,
                passthrough: false,
            },
        ]
        .to_vec()
    }

    fn outputs(&self) -> Vec<CmdOutput> {
        [
            CmdOutput {
                name: SIGNATURE.into(),
                r#type: ValueType::String,
            },
            CmdOutput {
                name: TX.into(),
                r#type: ValueType::String,
            },
        ]
        .to_vec()
    }

    async fn run(&self, ctx: Arc<Context>, inputs: ValueSet) -> flow::Result<ValueSet> {
        let input: Input = value::from_map(inputs)?;
        
        // ...

        Ok(value::to_map(&Output {
            signature,
            tx: tx_str,
        })?)
    }
}

inventory::submit!(CommandDescription::new(SOLANA_TRANSFER_SOL, |_| Box::new(
    TransferSol
)));

PreviousNative NodesNextNode Definition

Last updated 1 year ago

Was this helpful?

Then get the input from a with value::from_map :

Documentation on is useful.

ValueSet
https://serde.rs/