Code Template
Steps
1. Implement CommandTrait
:
CommandTrait
:fn name() -> Name
: return the name of the commandfn inputs() -> Vec<CmdInputDescription>
: return an array of input description, fields are:name
: name of the inputtype_bounds
: allowed typesrequired
passthrough
: iftrue
, this field will be present in the output
fn outputs() -> Vec<CmdOutputDescription>
:name
: name of the outputtype
: type of the output
async fn run(&self, ctx: Arc<Context>, inputs: ValueSet) -> Result<ValueSet, Error> {}
2. Submit with inventory::submit
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,
}
Then get the input from a ValueSet
with value::from_map
:
let input: Input = value::from_map(inputs)?;
4. Output is the same, but derive Serialize on it and use value::to_map
.
value::to_map
.Ok(value::to_map(&Output {
pubkey: keypair.pubkey(),
keypair,
})?)
Documentation on https://serde.rs/ is useful.
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
)));
Last updated
Was this helpful?