heavy-duty / znap

Performance-first Rust Framework to build APIs compatible with the Solana Actions Spec.
Apache License 2.0
60 stars 1 forks source link
[![Watch the demo](https://res.cloudinary.com/andresmgsl/image/upload/q_auto/f_auto/w_450/v1718845551/ZNAP_cuckvf.png)](https://youtu.be/pmuwP9fWa3M)

Znap

[Watch the demo](https://youtu.be/pmuwP9fWa3M)

Performance-first Rust Framework to build APIs compatible with the Solana Actions Spec.

Tutorials

Znap is an innovative Rust-based framework designed to simplify the creation of Solana Actions on the Solana blockchain.

If you're familiar with developing using the Anchor framework, then the experience will be familiar.

Getting Started

  1. cargo install znap-cli
  2. znap init <my-project-name>
  3. cd <my-project-name>
  4. znap new <collection-name>

Packages

Package Description Version Docs
znap Znap framework's core library to create Solana actions Crates.io Docs.rs
znap-syn Parsing and generating code for macros in Rust Crates.io Docs.rs
znap-macros Macro collection for creating Solana actions Crates.io Docs.rs
znap-cli Znap CLI to interact with a znap workspace. Crates.io Docs.rs

Example

use solana_sdk::{
    message::Message, native_token::LAMPORTS_PER_SOL, pubkey::Pubkey, system_instruction::transfer,
    transaction::Transaction,
};
use std::str::FromStr;
use znap::prelude::*;

#[collection]
pub mod my_actions {
    use super::*;

    pub fn send_donation(ctx: Context<SendDonationAction>) -> Result<ActionTransaction> {
        let account_pubkey = Pubkey::from_str(&ctx.payload.account)
            .or_else(|_| Err(Error::from(ActionError::InvalidAccountPublicKey)))?;
        let receiver_pubkey = Pubkey::from_str(&ctx.params.receiver_address)
            .or_else(|_| Err(Error::from(ActionError::InvalidReceiverPublicKey)))?;
        let transfer_instruction = transfer(
            &account_pubkey,
            &receiver_pubkey,
            ctx.query.amount * LAMPORTS_PER_SOL,
        );
        let transaction_message = Message::new(&[transfer_instruction], None);
        let transaction = Transaction::new_unsigned(transaction_message);

        Ok(ActionTransaction {
            transaction,
            message: Some("send donation to alice".to_string()),
        })
    }
}

#[derive(Action)]
#[action(
    icon = "https://media.discordapp.net/attachments/1205590693041541181/1212566609202520065/icon.png?ex=667eb568&is=667d63e8&hm=0f247078545828c0a5cf8300a5601c56bbc9b59d3d87a0c74b082df0f3a6d6bd&=&format=webp&quality=lossless&width=660&height=660",
    title = "Send a Donation to {{params.receiver_address}}",
    description = "Send a donation to {{params.receiver_address}} using the Solana blockchain via a Blink.",
    label = "Send",
    link = {
        label = "Send 1 SOL",
        href = "/api/send_donation/{{params.receiver_address}}?amount=1",
    },
    link = {
        label = "Send 5 SOL",
        href = "/api/send_donation/{{params.receiver_address}}?amount=5",
    },
    link = {
        label = "Send SOL",
        href = "/api/send_donation/{{params.receiver_address}}?amount={amount}",
        parameter = { label = "Amount in SOL", name = "amount" }
    },
)]
#[query(amount: u64)]
#[params(receiver_address: String)]
pub struct SendDonationAction;

#[derive(ErrorCode)]
enum ActionError {
    #[error(msg = "Invalid account public key")]
    InvalidAccountPublicKey,
    #[error(msg = "Invalid receiver public key")]
    InvalidReceiverPublicKey,
}