Skip to content

Commit

Permalink
swap (#164)
Browse files Browse the repository at this point in the history
  • Loading branch information
enzotar authored Nov 7, 2024
1 parent 33136c0 commit 1be9b4b
Show file tree
Hide file tree
Showing 6 changed files with 295 additions and 0 deletions.
28 changes: 28 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/cmds-solana/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ bundlr-sdk = { version = "=0.3.0", default-features = false, features = [

# branch: vendor
pyth-sdk-solana = { git = "https://github.com/space-operator/pyth-sdk-rs", rev = "84cc6fe07acbb8a81b216228be244bf039621560" }
jupiter-swap-api-client = { git = "https://github.com/jup-ag/jupiter-swap-api-client.git", package = "jupiter-swap-api-client"}

# anchor
anchor-lang = "=0.30.1"
Expand Down
118 changes: 118 additions & 0 deletions crates/cmds-solana/node-definitions/jupiter/swap.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
{
"type": "native",
"data": {
"node_definition_version": "0.1",
"unique_id": "",
"node_id": "jupiter_swap",
"version": "0.1",
"display_name": "Jupiter Swap",
"description": "",
"tags": [],
"related_to": [
{
"id": "",
"type": "",
"relationship": ""
}
],
"resources": {
"source_code_url": "",
"documentation_url": ""
},
"usage": {
"license": "Apache-2.0",
"license_url": "",
"pricing": {
"currency": "USDC",
"purchase_price": 0,
"price_per_run": 0,
"custom": {
"unit": "monthly",
"value": "0"
}
}
},
"authors": [
{
"name": "Space Operator",
"contact": ""
}
],
"design": null,
"options": null,
"instruction_info": {
"before": [],
"signature": "signature",
"after": []
}
},
"sources": [
{
"name": "signature",
"type": "signature",
"defaultValue": null,
"tooltip": "",
"optional": true
}
],
"targets": [
{
"name": "fee_payer",
"type_bounds": ["keypair"],
"required": true,
"defaultValue": null,
"tooltip": "",
"passthrough": false
},
{
"name": "input_mint",
"type_bounds": ["pubkey"],
"required": true,
"defaultValue": null,
"tooltip": "",
"passthrough": false
},
{
"name": "output_mint",
"type_bounds": ["pubkey"],
"required": true,
"defaultValue": null,
"tooltip": "",
"passthrough": false
},
{
"name": "auto_slippage",
"type_bounds": ["bool"],
"required": false,
"defaultValue": true,
"tooltip": "",
"passthrough": false
},
{
"name": "slippage_percent",
"type_bounds": ["u16"],
"required": false,
"defaultValue": null,
"tooltip": "",
"passthrough": false
},
{
"name": "amount",
"type_bounds": ["u64"],
"required": true,
"defaultValue": null,
"tooltip": "",
"passthrough": false
},
{
"name": "submit",
"type_bounds": ["bool"],
"required": false,
"defaultValue": true,
"tooltip": "",
"passthrough": false
}
],
"targets_form.ui_schema": {},
"targets_form.json_schema": {}
}
1 change: 1 addition & 0 deletions crates/cmds-solana/src/jupiter/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod swap;
146 changes: 146 additions & 0 deletions crates/cmds-solana/src/jupiter/swap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use crate::prelude::*;

use jupiter_swap_api_client::{
quote::{QuoteRequest, SwapMode},
swap::SwapRequest,
transaction_config::{ComputeUnitPriceMicroLamports, TransactionConfig},
JupiterSwapApiClient,
};
use tracing::info;

const NAME: &str = "jupiter_swap";

const DEFINITION: &str = flow_lib::node_definition!("jupiter/swap.json");

fn build() -> BuildResult {
static CACHE: BuilderCache = BuilderCache::new(|| {
CmdBuilder::new(DEFINITION)?
.check_name(NAME)?
.simple_instruction_info("signature")
});
Ok(CACHE.clone()?.build(run))
}

flow_lib::submit!(CommandDescription::new(NAME, |_| build()));

#[derive(Serialize, Deserialize, Debug)]
pub struct Input {
fee_payer: Wallet,
#[serde(with = "value::pubkey")]
input_mint: Pubkey,
#[serde(with = "value::pubkey")]
output_mint: Pubkey,
#[serde(default = "value::default::bool_true")]
auto_slippage: bool,
slippage_percent: Option<u16>,
amount: u64,
#[serde(default = "value::default::bool_true")]
submit: bool,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Output {
#[serde(default, with = "value::signature::opt")]
signature: Option<Signature>,
}

async fn run(mut ctx: Context, input: Input) -> Result<Output, CommandError> {
const API_BASE_URL: &str = "https://quote-api.jup.ag/v6";
const MAX_AUTO_SLIPPAGE_BPS: u16 = 300;
const DEXES: &str = "Whirlpool,Meteora DLMM,Raydium CLMM";

info!("Using base url: {}", API_BASE_URL);
info!("Using max auto slippage: {}", MAX_AUTO_SLIPPAGE_BPS);
info!("Using dexes: {}", DEXES);


let jupiter_swap_api_client = JupiterSwapApiClient::new(API_BASE_URL.into());

let mut quote_request = QuoteRequest {
amount: input.amount,
input_mint: input.input_mint,
output_mint: input.output_mint,
dexes: Some(DEXES.into()),
swap_mode: Some(SwapMode::ExactIn),
as_legacy_transaction: Some(true),
..QuoteRequest::default()
};

if input.auto_slippage {
quote_request.auto_slippage = Some(true);
quote_request.max_auto_slippage_bps = Some(MAX_AUTO_SLIPPAGE_BPS);
quote_request.compute_auto_slippage = true;
} else if let Some(slippage_percent) = input.slippage_percent {
quote_request.auto_slippage = Some(false);
quote_request.slippage_bps = slippage_percent as u16 * 100;

Check warning on line 75 in crates/cmds-solana/src/jupiter/swap.rs

View workflow job for this annotation

GitHub Actions / clippy

casting to the same type is unnecessary (`u16` -> `u16`)

warning: casting to the same type is unnecessary (`u16` -> `u16`) --> crates/cmds-solana/src/jupiter/swap.rs:75:38 | 75 | quote_request.slippage_bps = slippage_percent as u16 * 100; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slippage_percent` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast = note: `#[warn(clippy::unnecessary_cast)]` on by default

Check warning on line 75 in crates/cmds-solana/src/jupiter/swap.rs

View workflow job for this annotation

GitHub Actions / clippy

casting to the same type is unnecessary (`u16` -> `u16`)

warning: casting to the same type is unnecessary (`u16` -> `u16`) --> crates/cmds-solana/src/jupiter/swap.rs:75:38 | 75 | quote_request.slippage_bps = slippage_percent as u16 * 100; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slippage_percent` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast = note: `#[warn(clippy::unnecessary_cast)]` on by default
};

// GET /quote
let quote_response = jupiter_swap_api_client.quote(&quote_request).await.unwrap();
info!("{quote_response:#?}");

// POST /swap-instructions
let swap_instructions: jupiter_swap_api_client::swap::SwapInstructionsResponse =
jupiter_swap_api_client
.swap_instructions(&SwapRequest {
user_public_key: input.fee_payer.pubkey(),
quote_response,
config: TransactionConfig {
wrap_and_unwrap_sol: true,
allow_optimized_wrapped_sol_token_account: true,
compute_unit_price_micro_lamports: Some(ComputeUnitPriceMicroLamports::Auto),
dynamic_compute_unit_limit: true,
as_legacy_transaction: true,
..Default::default()
},
})
.await
.map_err(|e| anyhow::anyhow!(format!("Error getting swap instructions: {}", e)))?;

info!("swap_instructions: {swap_instructions:?}");

let mut instructions = Vec::new();
instructions.extend(swap_instructions.compute_budget_instructions);
instructions.extend(swap_instructions.setup_instructions);
instructions.push(swap_instructions.swap_instruction);
instructions.extend(swap_instructions.cleanup_instruction);

let ins = Instructions {
fee_payer: input.fee_payer.pubkey(),
signers: [input.fee_payer].into(),
instructions: instructions.into(),

Check warning on line 111 in crates/cmds-solana/src/jupiter/swap.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `std::vec::Vec<solana_sdk::instruction::Instruction>`

warning: useless conversion to the same type: `std::vec::Vec<solana_sdk::instruction::Instruction>` --> crates/cmds-solana/src/jupiter/swap.rs:111:23 | 111 | instructions: instructions.into(), | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `instructions` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `#[warn(clippy::useless_conversion)]` on by default

Check warning on line 111 in crates/cmds-solana/src/jupiter/swap.rs

View workflow job for this annotation

GitHub Actions / clippy

useless conversion to the same type: `std::vec::Vec<solana_sdk::instruction::Instruction>`

warning: useless conversion to the same type: `std::vec::Vec<solana_sdk::instruction::Instruction>` --> crates/cmds-solana/src/jupiter/swap.rs:111:23 | 111 | instructions: instructions.into(), | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `instructions` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion = note: `#[warn(clippy::useless_conversion)]` on by default
};

let ins = input.submit.then_some(ins).unwrap_or_default();

let signature = ctx.execute(ins, <_>::default()).await?.signature;

Ok(Output { signature })
}

// !! NOTE: Swap instructions not executable on devnet
//
// #[cfg(test)]
// mod tests {
// use solana_sdk::pubkey;

// use super::*;

// #[tokio::test]
// async fn test_build() {
// let ctx = Context::default();

// let payer: Wallet = Keypair::from_base58_string("4rQanLxTFvdgtLsGirizXejgYXACawB5ShoZgvz4wwXi4jnii7XHSyUFJbvAk4ojRiEAHvzK6Qnjq7UyJFNbydeQ").into();

// const INPUT_MINT: Pubkey = pubkey!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
// const OUTPUT_MINT: Pubkey = pubkey!("So11111111111111111111111111111111111111112");
// let input = value::map! {
// "fee_payer" => value::to_value(&payer).unwrap(),
// "input_mint" => INPUT_MINT,
// "output_mint" => OUTPUT_MINT,
// "amount" => 10,
// "submit" => true,
// };
// build().unwrap().run(ctx, input).await.unwrap();
// }
// }
1 change: 1 addition & 0 deletions crates/cmds-solana/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub mod record;
pub mod spl;
pub mod spl_token_2022;
pub mod streamflow;
pub mod jupiter;

pub use error::{Error, Result};

Expand Down

0 comments on commit 1be9b4b

Please sign in to comment.