Skip to content

Commit

Permalink
Merge pull request #93 from helius-labs/dev
Browse files Browse the repository at this point in the history
[Merge] Dev -> Main
  • Loading branch information
0xIchigo authored Nov 26, 2024
2 parents a38611d + ff7a0a9 commit 362fa32
Show file tree
Hide file tree
Showing 25 changed files with 822 additions and 148 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
path: target \
key: ${{ runner.os }}-target-${{ hashFiles('**/Cargo.lock') }} \
restore-keys: ${{ runner.os }}-target- || echo 'Cache target step failed or timed out.'"
- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
Expand Down Expand Up @@ -80,7 +80,7 @@ jobs:
run: cargo build --verbose

- name: Save build artifacts
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: build
path: target
Expand Down
39 changes: 29 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "helius"
version = "0.2.1"
version = "0.2.2"
edition = "2021"
description = "An asynchronous Helius Rust SDK for building the future of Solana"
keywords = ["helius", "solana", "asynchronous-sdk", "das", "cryptocurrency"]
Expand All @@ -17,24 +17,43 @@ bincode = "1.3.3"
chrono = { version = "0.4.11", features = ["serde"] }
futures = "0.3.30"
futures-util = "0.3.30"
mpl-token-metadata = { version = "5.0.0-beta.0" }
phf = { version = "0.11.2", features = ["macros"] }
rand = "0.8.5"
reqwest = { version = "0.12.3", features = ["json"] }
reqwest = { version = "0.12.8", features = ["json", "native-tls"] }
semver = "1.0.23"
serde = "1.0.198"
serde-enum-str = "0.4.0"
serde_json = "1.0.116"
solana-account-decoder = "1.18.12"
solana-client = "1.18.12"
solana-program = "1.18.12"
solana-rpc-client-api = "1.18.12"
solana-sdk = "1.18.11"
solana-transaction-status = "1.18.12"
solana-account-decoder = "2.0"
solana-client = "2.0"
solana-program = "2.0"
solana-rpc-client-api = "2.0"
solana-sdk = "2.0"
solana-transaction-status = "2.0"
thiserror = "1.0.58"
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread", "net"] }
tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread", "net", "time"] }
tokio-stream = "0.1.15"
tokio-tungstenite = { version = "0.21.0", features = ["native-tls", "handshake"] }
tokio-tungstenite = { version = "0.24.0", features = ["native-tls"] }
url = "2.5.0"
spl-token = { version = "6.0", features = ["no-entrypoint"] }

[dev-dependencies]
mockito = "1.4.0"

[features]
default = ["native-tls"]
native-tls = [
"reqwest/native-tls",
"tokio-tungstenite/native-tls"
]
rustls = [
"reqwest/rustls-tls",
"tokio-tungstenite/rustls-tls-webpki-roots"
]

[patch.crates-io]
# https://github.com/solana-labs/solana/issues/26688#issuecomment-2136066879
# For curve25519-dalek use the same revision Solana uses
# https://github.com/solana-labs/solana/blob/27eff8408b7223bb3c4ab70523f8a8dca3ca6645/Cargo.toml#L475-L563
curve25519-dalek = { git = "https://github.com/solana-labs/curve25519-dalek.git", rev = "b500cdc2a920cd5bff9e2dd974d7b97349d61464" }
33 changes: 25 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ where `x.y.z` is your desired version. Alternatively, use `cargo add helius` to

Remember to run `cargo update` regularly to fetch the latest version of the SDK.

### TLS Options
The Helius Rust SDK uses the native TLS implementation by default via:
```toml
[dependencies]
helius = "x.y.z"
```

However, the SDK also supports `rustls`. Add the following to your `Cargo.toml` to use `rustls` instead of the native TLS implementation:
```toml
[dependencies]
helius = { version = "x.y.z", default-features = false, features = ["rustls"] }
```

Using `rustls` may be preferred in environments where OpenSSL is not available or when a pure Rust TLS implementation is desired. However, it may not support all the same features as the native TLS implementation

## Usage
### `Helius`
The SDK provides a [`Helius`](https://github.com/helius-labs/helius-rust-sdk/blob/dev/src/client.rs) instance that can be configured with an API key and a given Solana cluster. Developers can generate a new API key on the [Helius Developer Dashboard](https://dev.helius.xyz/dashboard/app). This instance acts as the main entry point for interacting with the SDK by providing methods to access different Solana and RPC client functionalities. The following code is an example of how to use the SDK to fetch info on [Mad Lad #8420](https://explorer.solana.com/address/F9Lw3ki3hJ7PF9HQXsBzoY8GyE6sPoEZZdXJBsTTD2rk?network=mainnet):
Expand Down Expand Up @@ -118,17 +133,19 @@ Our SDK is designed to provide a seamless developer experience when building on
- [`remove_addresses_from_webhook`](https://github.com/helius-labs/helius-rust-sdk/blob/bf24259e3333ae93126bb65b342c2c63e80e07a6/src/webhook.rs#L75-L105) - Removes a list of addresses from an existing webhook by its ID

### Smart Transactions
- [`create_smart_transaction`](https://github.com/helius-labs/helius-rust-sdk/blob/705d66fb7d4004fc32c2a5f0d6ca4a1f2a7b175d/src/optimized_transaction.rs#L113-L312) - Creates an optimized transaction based on the provided configuration
- [`get_compute_units`](https://github.com/helius-labs/helius-rust-sdk/blob/a79a751e1a064125010bdb359068a366d635d005/src/optimized_transaction.rs#L29-L75) - Simulates a transaction to get the total compute units consumed
- [`poll_transaction_confirmation`](https://github.com/helius-labs/helius-rust-sdk/blob/a79a751e1a064125010bdb359068a366d635d005/src/optimized_transaction.rs#L77-L112) - Polls a transaction to check whether it has been confirmed in 5 second intervals with a 15 second timeout
- [`send_smart_transaction`](https://github.com/helius-labs/helius-rust-sdk/blob/705d66fb7d4004fc32c2a5f0d6ca4a1f2a7b175d/src/optimized_transaction.rs#L314-L374) - Builds and sends an optimized transaction, and handles its confirmation status
- [`create_smart_transaction`](https://github.com/helius-labs/helius-rust-sdk/blob/bd9e0b10c81ab9ea56dfcd286336b086f6737b64/src/optimized_transaction.rs#L131-L331) - Creates an optimized transaction based on the provided configuration
- [`get_compute_units`](https://github.com/helius-labs/helius-rust-sdk/blob/bd9e0b10c81ab9ea56dfcd286336b086f6737b64/src/optimized_transaction.rs#L34-L87) - Simulates a transaction to get the total compute units consumed
- [`poll_transaction_confirmation`](https://github.com/helius-labs/helius-rust-sdk/blob/bd9e0b10c81ab9ea56dfcd286336b086f6737b64/src/optimized_transaction.rs#L89-L129) - Polls a transaction to check whether it has been confirmed in 5 second intervals with a 15 second timeout
- [`send_smart_transaction`](https://github.com/helius-labs/helius-rust-sdk/blob/bd9e0b10c81ab9ea56dfcd286336b086f6737b64/src/optimized_transaction.rs#L333-L364) - Builds and sends an optimized transaction, and handles its confirmation status
- [`send_and_confirm_transaction`](https://github.com/helius-labs/helius-rust-sdk/blob/bd9e0b10c81ab9ea56dfcd286336b086f6737b64/src/optimized_transaction.rs#L366-L412) - Sends a transaction and handles its confirmation status with retry logic
- [`send_smart_transaction_with_seeds`](https://github.com/helius-labs/helius-rust-sdk/blob/bd9e0b10c81ab9ea56dfcd286336b086f6737b64/src/optimized_transaction.rs#L414-L487) - Sends a smart transaction using seed bytes

### Jito Smart Transactions and Helper Methods
- [`add_tip_instruction`](https://github.com/helius-labs/helius-rust-sdk/blob/02b351a5ee3fe16a36078b40f92dc72d0ad077ed/src/jito.rs#L66-L83) - Adds a tip instruction to the instructions provided
- [`create_smart_transaction_with_tip`](https://github.com/helius-labs/helius-rust-sdk/blob/02b351a5ee3fe16a36078b40f92dc72d0ad077ed/src/jito.rs#L85-L124) - Creates a smart transaction with a Jito tip
- [`get_bundle_statuses`](https://github.com/helius-labs/helius-rust-sdk/blob/02b351a5ee3fe16a36078b40f92dc72d0ad077ed/src/jito.rs#L169-L202) - Get the status of Jito bundles
- [`send_jito_bundle`](https://github.com/helius-labs/helius-rust-sdk/blob/02b351a5ee3fe16a36078b40f92dc72d0ad077ed/src/jito.rs#L126-L167) - Sends a bundle of transactions to the Jito Block Engine
- [`send_smart_transaction_with_tip`](https://github.com/helius-labs/helius-rust-sdk/blob/02b351a5ee3fe16a36078b40f92dc72d0ad077ed/src/jito.rs#L204-L269) - Sends a smart transaction as a Jito bundle with a tip
- [`create_smart_transaction_with_tip`](https://github.com/helius-labs/helius-rust-sdk/blob/bd9e0b10c81ab9ea56dfcd286336b086f6737b64/src/jito.rs#L85-L125) - Creates a smart transaction with a Jito tip
- [`get_bundle_statuses`](https://github.com/helius-labs/helius-rust-sdk/blob/bd9e0b10c81ab9ea56dfcd286336b086f6737b64/src/jito.rs#L170-L203) - Get the status of Jito bundles
- [`send_jito_bundle`](https://github.com/helius-labs/helius-rust-sdk/blob/bd9e0b10c81ab9ea56dfcd286336b086f6737b64/src/jito.rs#L127-L168) - Sends a bundle of transactions to the Jito Block Engine
- [`send_smart_transaction_with_tip`](https://github.com/helius-labs/helius-rust-sdk/blob/bd9e0b10c81ab9ea56dfcd286336b086f6737b64/src/jito.rs#L205-L270) - Sends a smart transaction as a Jito bundle with a tip

### Helper Methods
- [`get_priority_fee_estimate`](https://docs.helius.dev/solana-rpc-nodes/alpha-priority-fee-api) - Gets an estimate of the priority fees required for a transaction to be processed more quickly
Expand Down
140 changes: 140 additions & 0 deletions examples/delegate_and_revoke_collection_authority.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use helius::error::Result;
use helius::types::Cluster;
use helius::utils::collection_authority::{get_collection_authority_record, get_collection_metadata_account};
use helius::Helius;
use mpl_token_metadata::instructions::CreateMetadataAccountV3;
use mpl_token_metadata::instructions::CreateMetadataAccountV3InstructionArgs;
use mpl_token_metadata::types::DataV2;
use solana_program::system_instruction::create_account;
use solana_program::system_program;
use solana_sdk::{
signature::{Keypair, Signer},
transaction::Transaction,
};
use spl_token::solana_program::program_pack::Pack;
use spl_token::{instruction::initialize_mint, state::Mint};

#[tokio::main]
async fn main() -> Result<()> {
let api_key = "";
let payer = Keypair::from_base58_string("");
let cluster = Cluster::MainnetBeta;
let helius = Helius::new_with_async_solana(api_key, cluster)?;
let rpc_client = helius.async_connection()?;

let collection_mint_keypair = Keypair::new();
let rent = rpc_client
.get_minimum_balance_for_rent_exemption(Mint::LEN)
.await
.expect("Failed to get rent exemption amount");
let create_mint_account_ix = create_account(
&payer.pubkey(),
&collection_mint_keypair.pubkey(),
rent,
Mint::LEN as u64,
&spl_token::id(),
);
let collection_authority_keypair = Keypair::new();
let initialize_mint_ix = initialize_mint(
&spl_token::id(),
&collection_mint_keypair.pubkey(),
&collection_authority_keypair.pubkey(),
None,
9,
)
.expect("Failed to create initialize mint instruction");
let recent_blockhash = rpc_client.get_latest_blockhash().await?;
let transaction = Transaction::new_signed_with_payer(
&[create_mint_account_ix, initialize_mint_ix],
Some(&payer.pubkey()),
&[&payer, &collection_mint_keypair],
recent_blockhash,
);
rpc_client
.send_and_confirm_transaction(&transaction)
.await
.expect("Failed to create and initialize mint");
println!(
"Collection mint created and initialized: {}",
collection_mint_keypair.pubkey()
);

let metadata_pubkey = get_collection_metadata_account(&collection_mint_keypair.pubkey());
let create_metadata_accounts_ix = CreateMetadataAccountV3 {
metadata: metadata_pubkey,
mint: collection_mint_keypair.pubkey(),
mint_authority: collection_authority_keypair.pubkey(),
payer: payer.pubkey(),
update_authority: (collection_authority_keypair.pubkey(), true),
system_program: system_program::ID,
rent: None,
}
.instruction(CreateMetadataAccountV3InstructionArgs {
data: DataV2 {
name: "".to_string(),
symbol: "".to_string(),
uri: "".to_string(),
seller_fee_basis_points: 0,
creators: None,
collection: None,
uses: None,
},
is_mutable: true,
collection_details: None,
});
let recent_blockhash = rpc_client.get_latest_blockhash().await?;
let transaction = Transaction::new_signed_with_payer(
&[create_metadata_accounts_ix],
Some(&payer.pubkey()),
&[&payer, &collection_authority_keypair],
recent_blockhash,
);
rpc_client.send_and_confirm_transaction(&transaction).await?;
println!("Metadata account created: {}", metadata_pubkey.to_string());

let delegated_authority_keypair = Keypair::new();
let result = helius
.delegate_collection_authority(
collection_mint_keypair.pubkey(),
delegated_authority_keypair.pubkey(),
&collection_authority_keypair,
Some(&payer),
)
.await;
assert!(
result.is_ok(),
"Failed to delegate collection authority to {}",
delegated_authority_keypair.pubkey()
);
println!(
"Delegate collection authority to {} transaction signature: {}",
delegated_authority_keypair.pubkey(),
result?
);

let collection_authority_record =
get_collection_authority_record(&collection_mint_keypair.pubkey(), &delegated_authority_keypair.pubkey());
let account = rpc_client.get_account(&collection_authority_record).await;
assert!(account.is_ok(), "Collection authority record account should exist");

let result = helius
.revoke_collection_authority(
collection_mint_keypair.pubkey(),
Some(delegated_authority_keypair.pubkey()),
&collection_authority_keypair,
Some(&payer),
)
.await;
assert!(result.is_ok(), "Failed to revoke collection authority");
println!(
"Revoke collection authority from {} transaction signature: {}",
delegated_authority_keypair.pubkey(),
result?
);

let account = rpc_client.get_account(&collection_authority_record).await;
assert!(account.is_err(), "Collection authority record account should be closed");

println!("Delegated collection authority successfully revoked");
Ok(())
}
2 changes: 1 addition & 1 deletion examples/enhanced_websocket_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ async fn main() -> Result<()> {
let api_key: &str = "your_api_key";
let cluster: Cluster = Cluster::MainnetBeta;

let helius: Helius = Helius::new_with_ws(api_key, cluster).await.unwrap();
let helius: Helius = Helius::new_with_ws(api_key, cluster).await?;

let key: pubkey::Pubkey = pubkey!("BtsmiEEvnSuUnKxqXj2PZRYpPJAc7C34mGz8gtJ1DAaH");

Expand Down
4 changes: 3 additions & 1 deletion examples/send_smart_transaction_with_tip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use solana_sdk::{
system_instruction::transfer,
};
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
use tokio::time::sleep;

Expand All @@ -28,7 +29,7 @@ async fn main() {

let create_config: CreateSmartTransactionConfig = CreateSmartTransactionConfig {
instructions,
signers: vec![&from_keypair],
signers: vec![Arc::new(from_keypair)],
lookup_tables: None,
fee_payer: None,
};
Expand All @@ -42,6 +43,7 @@ async fn main() {
max_retries: None,
min_context_slot: None,
},
timeout: Timeout::default(),
};

// Send the optimized transaction with a 10k lamport tip using the New York region's API URL
Expand Down
12 changes: 9 additions & 3 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ impl Helius {
/// ```
pub fn new(api_key: &str, cluster: Cluster) -> Result<Self> {
let config: Arc<Config> = Arc::new(Config::new(api_key, cluster)?);
let client: Client = Client::new();
let client: Client = Client::builder().build().map_err(HeliusError::ReqwestError)?;
let rpc_client: Arc<RpcClient> = Arc::new(RpcClient::new(Arc::new(client.clone()), config.clone())?);

Ok(Helius {
config,
client,
Expand Down Expand Up @@ -75,7 +76,7 @@ impl Helius {
/// ```
pub fn new_with_async_solana(api_key: &str, cluster: Cluster) -> Result<Self> {
let config: Arc<Config> = Arc::new(Config::new(api_key, cluster)?);
let client: Client = Client::new();
let client: Client = Client::builder().build().map_err(HeliusError::ReqwestError)?;
let url: String = format!("{}/?api-key={}", config.endpoints.rpc, config.api_key);
let async_solana_client: Arc<AsyncSolanaRpcClient> = Arc::new(AsyncSolanaRpcClient::new(url));

Expand All @@ -98,10 +99,11 @@ impl Helius {
/// An instance of `Helius` if successful. A `HeliusError` is returned if an error occurs during configuration or initialization of the HTTP, RPC, or WS client
pub async fn new_with_ws(api_key: &str, cluster: Cluster) -> Result<Self> {
let config: Arc<Config> = Arc::new(Config::new(api_key, cluster)?);
let client: Client = Client::new();
let client: Client = Client::builder().build().map_err(HeliusError::ReqwestError)?;
let rpc_client: Arc<RpcClient> = Arc::new(RpcClient::new(Arc::new(client.clone()), config.clone())?);
let wss: String = format!("{}{}", ENHANCED_WEBSOCKET_URL, api_key);
let ws_client: Arc<EnhancedWebsocket> = Arc::new(EnhancedWebsocket::new(&wss).await?);

Ok(Helius {
config,
client,
Expand Down Expand Up @@ -144,6 +146,10 @@ impl Helius {
pub fn ws(&self) -> Option<Arc<EnhancedWebsocket>> {
self.ws_client.clone()
}

pub fn config(&self) -> Arc<Config> {
self.config.clone()
}
}

/// A wrapper around the asynchronous Solana RPC client that provides thread-safe access
Expand Down
6 changes: 5 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::error::{HeliusError, Result};
use crate::types::{Cluster, HeliusEndpoints};
use crate::types::{Cluster, HeliusEndpoints, MintApiAuthority};

/// Configuration settings for the Helius client
///
Expand Down Expand Up @@ -40,4 +40,8 @@ impl Config {
endpoints,
})
}

pub fn mint_api_authority(&self) -> MintApiAuthority {
MintApiAuthority::from_cluster(&self.cluster)
}
}
13 changes: 13 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ pub enum HeliusError {

#[error("Url parse error")]
UrlParseError(#[from] url::ParseError),

#[error("TLS error: {0}")]
TlsError(String),
}

impl HeliusError {
Expand Down Expand Up @@ -156,5 +159,15 @@ impl From<SanitizeError> for HeliusError {
}
}

impl From<ReqwestError> for HeliusError {
fn from(err: reqwest::Error) -> Self {
if err.is_builder() {
HeliusError::TlsError(err.to_string())
} else {
HeliusError::ReqwestError(err)
}
}
}

/// A handy type alias for handling results across the Helius SDK
pub type Result<T> = std::result::Result<T, HeliusError>;
Loading

0 comments on commit 362fa32

Please sign in to comment.