Skip to content

Instantly share code, notes, and snippets.

@ligulfzhou
Created January 5, 2025 12:06
Show Gist options
  • Select an option

  • Save ligulfzhou/fd12b5abf9e6d3d5d9817a8b8046d9e5 to your computer and use it in GitHub Desktop.

Select an option

Save ligulfzhou/fd12b5abf9e6d3d5d9817a8b8046d9e5 to your computer and use it in GitHub Desktop.
swap on bluemove
use anyhow::bail;
use fastcrypto::hash::HashFunction;
use move_core_types::identifier::Identifier;
use move_core_types::language_storage::TypeTag;
use shared_crypto::intent::{Intent, IntentMessage};
use std::str::FromStr;
use sui_sdk::types::base_types::{ObjectID, SequenceNumber};
use sui_sdk::types::transaction::{Argument, ProgrammableTransaction};
use sui_sdk::{
rpc_types::{Coin, SuiTransactionBlockResponseOptions},
types::{
base_types::SuiAddress,
crypto::{get_key_pair_from_rng, DefaultHash, Signer, SuiKeyPair},
programmable_transaction_builder::ProgrammableTransactionBuilder,
quorum_driver_types::ExecuteTransactionRequestType,
transaction::{Command, ObjectArg, Transaction, TransactionData},
},
SuiClient, SuiClientBuilder, SUI_COIN_TYPE,
};
const RPC: &str = "https://fullnode.mainnet.sui.io:443";
pub const BLUEMOVE_PACKAGE: &str =
"0x08cd33481587d4c4612865b164796d937df13747d8c763b8a178c87e3244498f";
pub const BLUEMOVE_VAULT_PACKAGE: &str =
"0x7ea6e27ad7af6f3b8671d59df1aaebd7c03dddab893e52a714227b2f4fe91519";
pub const BLUEMOVE_VAULT_OBJECT_ID: &str =
"0x39a3c55742c0e011b6f65548e73cf589e1ae5e82dbfab449ca57f24c3bcd9514";
pub const BLUEMOVE_VAULT_OBJECT_INIT_VERSION: u64 = 335738127;
pub const BLUEMOVE_CONFIG_OBJECT_ID: &str =
"0x0f8fc23dbcc9362b72c7a4c5aa53fcefa02ebfbb83a812c8c262ccd2c076d9ee";
pub const BLUEMOVE_CONFIG_OBJECT_INIT_VERSION: u64 = 335738127;
pub const BLUEMOVE_SWAP_DEX_INFO_OBJECT_ID: &str =
"0x3f2d9f724f4a1ce5e71676448dc452be9a6243dac9c5b975a588c8c867066e92";
pub const BLUEMOVE_SWAP_DEX_INFO_OBJECT_VERSION: u64 = 1587827;
pub const ONE_PACKAGE: &str = "0x0000000000000000000000000000000000000000000000000000000000000001";
pub const TWO_PACKAGE: &str = "0x0000000000000000000000000000000000000000000000000000000000000002";
// 获取用户的token,可能会有非常多的碎片
async fn get_all_coins(
client: &SuiClient,
address: SuiAddress,
coin_type: &str,
) -> anyhow::Result<Vec<Coin>> {
let mut cursor = None;
let mut coins = vec![];
loop {
let coins_res = client
.coin_read_api()
.get_coins(address, Some(coin_type.to_string()), cursor, None) // default limit is 50
.await?;
coins.extend(coins_res.data);
if coins_res.has_next_page {
cursor = coins_res.next_cursor;
continue;
}
return Ok(coins);
}
}
async fn send_tx(
tx: ProgrammableTransaction,
keypair: &SuiKeyPair,
gas_coin: &[Coin],
client: &SuiClient,
gas_budget: u64,
) -> anyhow::Result<String> {
let gas_price = client.read_api().get_reference_gas_price().await?;
// create the transaction data that will be sent to the network
let tx_data = TransactionData::new_programmable(
SuiAddress::from(keypair.public()),
gas_coin
.iter()
.map(|coin| coin.object_ref())
.collect::<Vec<_>>(),
tx,
gas_budget,
gas_price,
);
// 4) sign transaction
let intent_msg = IntentMessage::new(Intent::sui_transaction(), tx_data);
let raw_tx = bcs::to_bytes(&intent_msg).expect("bcs should not fail");
let mut hasher = DefaultHash::default();
hasher.update(raw_tx.clone());
let digest = hasher.finalize().digest;
let sig = keypair.sign(&digest);
// 5) execute the transaction
println!("Executing the transaction...");
let transaction_response = client
.quorum_driver_api()
.execute_transaction_block(
Transaction::from_data(tx_data, vec![sig]),
SuiTransactionBlockResponseOptions::full_content(),
Some(ExecuteTransactionRequestType::WaitForLocalExecution),
)
.await?;
let hash = transaction_response.digest.to_string();
println!("transaction hash: {:?}", hash);
Ok(hash)
}
// 买token
async fn buy_token(
keypair: &SuiKeyPair,
client: &SuiClient,
coin_type: &str,
buy_amount: u64,
) -> anyhow::Result<()> {
// no need to get all gas_coins to pay the gas
let gas_coins =
get_all_coins(&client, SuiAddress::from(keypair.public()), SUI_COIN_TYPE).await?;
let mut ptb = ProgrammableTransactionBuilder::new();
// 1: 切出要花的sui
let split_cmd = Command::SplitCoins(Argument::GasCoin, vec![ptb.pure(buy_amount).unwrap()]);
ptb.command(split_cmd);
let value_cmd = Command::move_call(
ObjectID::from_hex_literal(TWO_PACKAGE)?,
Identifier::new("coin")?,
Identifier::new("value")?,
vec![TypeTag::from_str(SUI_COIN_TYPE)?],
vec![Argument::NestedResult(0, 0)],
);
ptb.command(value_cmd);
// 3: swap_exact_input_
let swap_cmd = Command::move_call(
ObjectID::from_hex_literal(BLUEMOVE_PACKAGE)?,
Identifier::new("router")?,
Identifier::new("swap_exact_input_")?,
vec![
TypeTag::from_str(SUI_COIN_TYPE)?,
TypeTag::from_str(coin_type)?,
],
vec![
Argument::NestedResult(1, 0),
Argument::NestedResult(0, 0),
ptb.pure(0u64)?,
ptb.obj(ObjectArg::SharedObject {
id: ObjectID::from_str(BLUEMOVE_SWAP_DEX_INFO_OBJECT_ID)?,
initial_shared_version: SequenceNumber::from_u64(
BLUEMOVE_SWAP_DEX_INFO_OBJECT_VERSION,
),
mutable: true,
})?,
],
);
ptb.command(swap_cmd);
// 4: some
let swap_cmd = Command::move_call(
ObjectID::from_hex_literal(ONE_PACKAGE)?,
Identifier::new("option")?,
Identifier::new("some")?,
vec![TypeTag::Address],
vec![ptb.pure(keypair.public())?],
);
ptb.command(swap_cmd);
// 5: settle
let settle_cmd = Command::move_call(
ObjectID::from_hex_literal(BLUEMOVE_VAULT_PACKAGE)?,
Identifier::new("settle")?,
Identifier::new("settle")?,
vec![
TypeTag::from_str(SUI_COIN_TYPE)?,
TypeTag::from_str(coin_type)?,
],
vec![
ptb.obj(ObjectArg::SharedObject {
id: ObjectID::from_str(BLUEMOVE_CONFIG_OBJECT_ID)?,
initial_shared_version: SequenceNumber::from_u64(
BLUEMOVE_CONFIG_OBJECT_INIT_VERSION,
),
mutable: false,
})
.unwrap(),
ptb.obj(ObjectArg::SharedObject {
id: ObjectID::from_str(BLUEMOVE_VAULT_OBJECT_ID)?,
initial_shared_version: SequenceNumber::from_u64(
BLUEMOVE_VAULT_OBJECT_INIT_VERSION,
),
mutable: true,
})?,
ptb.pure(buy_amount)?,
Argument::NestedResult(2, 0),
ptb.pure(0u64)?,
ptb.pure(u64::MAX)?,
Argument::NestedResult(3, 0),
ptb.pure(0u64)?,
],
);
ptb.command(settle_cmd);
let transfer_cmd = Command::TransferObjects(
vec![Argument::NestedResult(2, 0)],
ptb.pure(keypair.public())?,
);
ptb.command(transfer_cmd);
let pt = ptb.finish();
let hash = send_tx(pt, keypair, &gas_coins, &client, 400_0000).await?;
println!("Transaction hash: {:?}", hash);
Ok(())
}
// 卖token
async fn sell_token(
keypair: &SuiKeyPair,
client: &SuiClient,
coin_type: &str,
sell_amount: u64,
) -> anyhow::Result<()> {
// no need to get all gas_coins to pay the gas
let gas_coins =
get_all_coins(&client, SuiAddress::from(keypair.public()), SUI_COIN_TYPE).await?;
let coins = get_all_coins(&client, SuiAddress::from(keypair.public()), coin_type).await?;
if coins.is_empty() {
bail!("No coins to sell");
}
let mut ptb = ProgrammableTransactionBuilder::new();
// 1: 如果有多个碎片,可以先merge一下
let add_idx = if coins.len() > 1 {
let merge_coin_cmd = Command::MergeCoins(
ptb.obj(ObjectArg::ImmOrOwnedObject(coins[0].object_ref()))?,
coins[1..]
.iter()
.map(|coin| {
ptb.obj(ObjectArg::ImmOrOwnedObject(coin.object_ref()))
.unwrap()
})
.collect::<Vec<_>>(),
);
ptb.command(merge_coin_cmd);
1
} else {
0
};
// 2: 切出要卖掉的coin
let split_cmd = Command::SplitCoins(
ptb.obj(ObjectArg::ImmOrOwnedObject(coins[0].object_ref()))?,
vec![ptb.pure(sell_amount).unwrap()],
);
ptb.command(split_cmd);
// 3: value
let value_cmd = Command::move_call(
ObjectID::from_hex_literal(TWO_PACKAGE)?,
Identifier::new("coin")?,
Identifier::new("value")?,
vec![TypeTag::from_str(coin_type).unwrap()],
vec![Argument::NestedResult(add_idx, 0)],
);
ptb.command(value_cmd);
// 3: swap_exact_input_
let swap_cmd = Command::move_call(
ObjectID::from_hex_literal(fk_sui::consts::BLUEMOVE_PACKAGE)?,
Identifier::new("router")?,
Identifier::new("swap_exact_input_")?,
vec![
TypeTag::from_str(coin_type)?,
TypeTag::from_str(SUI_COIN_TYPE)?,
],
vec![
Argument::NestedResult(1 + add_idx, 0),
Argument::NestedResult(add_idx, 0),
ptb.pure(0u64)?,
ptb.obj(ObjectArg::SharedObject {
id: ObjectID::from_str(BLUEMOVE_SWAP_DEX_INFO_OBJECT_ID)?,
initial_shared_version: SequenceNumber::from_u64(
BLUEMOVE_SWAP_DEX_INFO_OBJECT_VERSION,
),
mutable: true,
})?,
],
);
ptb.command(swap_cmd);
// 4: some
let swap_cmd = Command::move_call(
ObjectID::from_hex_literal(ONE_PACKAGE)?,
Identifier::new("option")?,
Identifier::new("some")?,
vec![TypeTag::Address],
vec![ptb.pure(SuiAddress::from(keypair.public()))?],
);
ptb.command(swap_cmd);
// 5: settle
let settle_cmd = Command::move_call(
ObjectID::from_hex_literal(BLUEMOVE_VAULT_PACKAGE)?,
Identifier::new("settle")?,
Identifier::new("settle")?,
vec![
TypeTag::from_str(coin_type)?,
TypeTag::from_str(SUI_COIN_TYPE)?,
],
vec![
ptb.obj(ObjectArg::SharedObject {
id: ObjectID::from_str(BLUEMOVE_CONFIG_OBJECT_ID)?,
initial_shared_version: SequenceNumber::from_u64(
BLUEMOVE_CONFIG_OBJECT_INIT_VERSION,
),
mutable: false,
})?,
ptb.obj(ObjectArg::SharedObject {
id: ObjectID::from_str(BLUEMOVE_VAULT_OBJECT_ID)?,
initial_shared_version: SequenceNumber::from_u64(
BLUEMOVE_VAULT_OBJECT_INIT_VERSION,
),
mutable: true,
})?,
ptb.pure(sell_amount).unwrap(),
Argument::NestedResult(2 + add_idx, 0),
ptb.pure(0u64).unwrap(),
ptb.pure(u64::MAX).unwrap(),
Argument::NestedResult(3 + add_idx, 0),
ptb.pure(0u64).unwrap(),
],
);
ptb.command(settle_cmd);
let transfer_cmd = Command::TransferObjects(
vec![Argument::NestedResult(2 + add_idx, 0)],
ptb.pure(SuiAddress::from(keypair.public())).unwrap(),
);
ptb.command(transfer_cmd);
let pt = ptb.finish();
let hash = send_tx(pt, keypair, &gas_coins, &client, 400_0000).await?;
println!("Transaction hash: {:?}", hash);
Ok(())
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let keypair = SuiKeyPair::Ed25519(get_key_pair_from_rng(&mut rand::rngs::OsRng).1);
let client = SuiClientBuilder::default().build(RPC).await?;
let coin_to_trade: &str =
"0xa8b69040684d576828475115b30cc4ce7c7743eab9c7d669535ee31caccef4f5::suiman::SUIMAN";
// 花 1_0000_0000(0.1sui) 买 某coin
buy_token(&keypair, &client, coin_to_trade, 1_0000_0000).await?;
// 出售 100 个某coin
sell_token(&keypair, &client, coin_to_trade, 100).await?;
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment