# 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`&#x20;
   4. `passthrough` : if `true`, this field will be present in the output
3. `fn outputs() -> Vec<CmdOutputDescription>`:&#x20;
   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:

```rust
#[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,
}
```

Then get the input from a [`ValueSet`](https://docs.spaceoperator.com/visual-builder/nodes/native-nodes/valueset) with `value::from_map` :&#x20;

```rust
 let input: Input = value::from_map(inputs)?;
```

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

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

Documentation on <https://serde.rs/> is useful.

### Example

<figure><img src="https://265593456-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5y592680M9yE8eRuq9kP%2Fuploads%2FCuLTVjlmRNc6hKQLC8lJ%2FScreenshot%20from%202022-12-13%2016-11-10.png?alt=media&#x26;token=6c1cb545-9a6b-4654-884d-f950c8b8ca0d" alt=""><figcaption></figcaption></figure>

```rust
// 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
)));

```
