icepick-solana: add support for utilizing durable nonces
This commit is contained in:
parent
be5f9a9fa0
commit
ca0fc3eef9
|
@ -12,6 +12,7 @@ icepick-module = { version = "0.1.0", path = "../../icepick-module" }
|
|||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json.workspace = true
|
||||
solana-rpc-client = { version = "2.1.1", default-features = false }
|
||||
solana-rpc-client-nonce-utils = "2.1.7"
|
||||
solana-sdk = { version = "2.1.1" }
|
||||
solana-transaction-status = "2.1.1"
|
||||
solana-transaction-status-client-types = "2.1.1"
|
||||
|
|
|
@ -129,6 +129,21 @@ impl std::fmt::Display for Cluster {
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: While, technically, they both fit in the same width, it is _important_ to have different
|
||||
// functionality based on which is provided, as Nonce requires an incremention instruction.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(rename_all = "snake_case", untagged)]
|
||||
pub enum Hashable {
|
||||
Nonce {
|
||||
nonce_data: String,
|
||||
nonce_address: String,
|
||||
nonce_authority: String,
|
||||
},
|
||||
Blockhash {
|
||||
blockhash: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetBlockhash {
|
||||
cluster: Option<Cluster>,
|
||||
|
@ -161,6 +176,12 @@ pub struct CreateNonceAccountAndSigningKey {
|
|||
from_address: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetNonceAccountData {
|
||||
nonce_address: String,
|
||||
cluster: Option<Cluster>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct FindNonceAccounts {
|
||||
authorization_address: String,
|
||||
|
@ -198,6 +219,19 @@ pub struct TransferToken {
|
|||
fee_payer_address: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Compile {
|
||||
#[serde(flatten)]
|
||||
hashable: Hashable,
|
||||
derivation_accounts: Vec<u32>,
|
||||
instructions: Vec<solana_sdk::instruction::Instruction>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Inspect {
|
||||
transaction: solana_sdk::transaction::Transaction,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Sign {
|
||||
blockhash: String,
|
||||
|
@ -231,10 +265,13 @@ pub enum Operation {
|
|||
AwaitFunds(AwaitFunds),
|
||||
GetTokenInfo(GetTokenInfo),
|
||||
CreateNonceAccountAndSigningKey(CreateNonceAccountAndSigningKey),
|
||||
GetNonceAccountData(GetNonceAccountData),
|
||||
FindNonceAccounts(FindNonceAccounts),
|
||||
Transfer(Transfer),
|
||||
CreateTokenAccount(CreateTokenAccount),
|
||||
TransferToken(TransferToken),
|
||||
Compile(Compile),
|
||||
Inspect(Inspect),
|
||||
Sign(Sign),
|
||||
Broadcast(Broadcast),
|
||||
}
|
||||
|
@ -292,135 +329,148 @@ impl Module for Solana {
|
|||
.to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
};
|
||||
vec![
|
||||
icepick_module::help::Operation {
|
||||
name: "get-blockhash".to_string(),
|
||||
description: "Get the latest blockhash".to_string(),
|
||||
arguments: vec![cluster.clone()],
|
||||
},
|
||||
icepick_module::help::Operation {
|
||||
name: "generate-wallet".to_string(),
|
||||
description: "Generate the derivation index for a wallet.".to_string(),
|
||||
arguments: vec![Argument {
|
||||
name: "account".to_string(),
|
||||
description: "The derivation account used for generating the wallet."
|
||||
.to_string(),
|
||||
r#type: ArgumentType::Optional,
|
||||
}],
|
||||
},
|
||||
icepick_module::help::Operation {
|
||||
name: "get-wallet-address".to_string(),
|
||||
description: "Get the address for a given wallet.".to_string(),
|
||||
arguments: vec![],
|
||||
},
|
||||
icepick_module::help::Operation {
|
||||
name: "await-funds".to_string(),
|
||||
description: "Await a minimum amount of funds in an account".to_string(),
|
||||
arguments: vec![Argument {
|
||||
let get_blockhash = icepick_module::help::Operation {
|
||||
name: "get-blockhash".to_string(),
|
||||
description: "Get the latest blockhash".to_string(),
|
||||
arguments: vec![cluster.clone()],
|
||||
};
|
||||
let generate_wallet = icepick_module::help::Operation {
|
||||
name: "generate-wallet".to_string(),
|
||||
description: "Generate the derivation index for a wallet.".to_string(),
|
||||
arguments: vec![Argument {
|
||||
name: "account".to_string(),
|
||||
description: "The derivation account used for generating the wallet.".to_string(),
|
||||
r#type: ArgumentType::Optional,
|
||||
}],
|
||||
};
|
||||
let get_wallet_address = icepick_module::help::Operation {
|
||||
name: "get-wallet-address".to_string(),
|
||||
description: "Get the address for a given wallet.".to_string(),
|
||||
arguments: vec![],
|
||||
};
|
||||
let await_funds = icepick_module::help::Operation {
|
||||
name: "await-funds".to_string(),
|
||||
description: "Await a minimum amount of funds in an account".to_string(),
|
||||
arguments: vec![
|
||||
Argument {
|
||||
name: "address".to_string(),
|
||||
description: "The address to monitor".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
}, Argument {
|
||||
},
|
||||
Argument {
|
||||
name: "amount".to_string(),
|
||||
description: "The amount of lamports to await".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
}],
|
||||
},
|
||||
icepick_module::help::Operation {
|
||||
name: "get-token-info".to_string(),
|
||||
description: "Get the address for a given token.".to_string(),
|
||||
arguments: vec![Argument {
|
||||
name: "token".to_string(),
|
||||
description: "The token to look up".to_string(),
|
||||
},
|
||||
],
|
||||
};
|
||||
let get_token_info = icepick_module::help::Operation {
|
||||
name: "get-token-info".to_string(),
|
||||
description: "Get the address for a given token.".to_string(),
|
||||
arguments: vec![Argument {
|
||||
name: "token".to_string(),
|
||||
description: "The token to look up".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
}],
|
||||
};
|
||||
let create_nonce_account_and_signing_key = icepick_module::help::Operation {
|
||||
name: "create-nonce-account-and-signing-key".to_string(),
|
||||
description: "Create a nonce account for signing durable transactions".to_string(),
|
||||
arguments: vec![
|
||||
account.clone(),
|
||||
from_address.clone(),
|
||||
Argument {
|
||||
name: "authorization_address".to_string(),
|
||||
description: "The account authorized to use and advance the nonce.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
}],
|
||||
},
|
||||
icepick_module::help::Operation {
|
||||
name: "create-nonce-account-and-signing-key".to_string(),
|
||||
description: "Create a nonce account for signing durable transactions".to_string(),
|
||||
arguments: vec![
|
||||
account.clone(),
|
||||
from_address.clone(),
|
||||
Argument {
|
||||
name: "authorization_address".to_string(),
|
||||
description: "The account authorized to use and advance the nonce.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
],
|
||||
},
|
||||
icepick_module::help::Operation {
|
||||
name: "find-nonce-accounts".to_string(),
|
||||
description: "Find all nonce accounts for an authorized address".to_string(),
|
||||
arguments: vec![
|
||||
cluster.clone(),
|
||||
Argument {
|
||||
name: "authorization_address".to_string(),
|
||||
description: "The account authorized to use and advance nonces.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
],
|
||||
},
|
||||
icepick_module::help::Operation {
|
||||
name: "transfer".to_string(),
|
||||
description: "Transfer SOL from a Keyfork wallet to an external wallet."
|
||||
.to_string(),
|
||||
arguments: vec![
|
||||
Argument {
|
||||
name: "amount".to_string(),
|
||||
description: "The amount of SOL to transfer.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
account.clone(),
|
||||
Argument {
|
||||
name: "to_address".to_string(),
|
||||
description: "The address to send SOL to.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
from_address.clone(),
|
||||
fee.clone(),
|
||||
fee_payer.clone(),
|
||||
fee_payer_address.clone(),
|
||||
],
|
||||
},
|
||||
icepick_module::help::Operation {
|
||||
name: "stake".to_string(),
|
||||
description: "Stake SOL to earn rewards.".to_string(),
|
||||
arguments: vec![
|
||||
Argument {
|
||||
name: "amount".to_string(),
|
||||
description: "The amount of SOL to stake.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
account.clone(),
|
||||
from_address.clone(),
|
||||
fee.clone(),
|
||||
fee_payer.clone(),
|
||||
fee_payer_address.clone(),
|
||||
],
|
||||
},
|
||||
// kinda BS that you have to make an account for a token, but ok.
|
||||
icepick_module::help::Operation {
|
||||
name: "create-token-account".to_string(),
|
||||
description: "Create an account for a given token".to_string(),
|
||||
arguments: vec![
|
||||
Argument {
|
||||
name: "wallet_address".to_string(),
|
||||
description: "The address of the token.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
Argument {
|
||||
name: "token_address".to_string(),
|
||||
description: "The address of the token.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
Argument {
|
||||
name: "funder_address".to_string(),
|
||||
description: "The address of the funder (signer).".to_string(),
|
||||
r#type: ArgumentType::Optional,
|
||||
},
|
||||
],
|
||||
},
|
||||
icepick_module::help::Operation {
|
||||
},
|
||||
],
|
||||
};
|
||||
let get_nonce_account_data = icepick_module::help::Operation {
|
||||
name: "get-nonce-account-data".to_string(),
|
||||
description: "Get the data for a nonce account".to_string(),
|
||||
arguments: vec![
|
||||
cluster.clone(),
|
||||
Argument {
|
||||
name: "nonce_address".to_string(),
|
||||
description: "The address of the nonce account.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
],
|
||||
};
|
||||
let find_nonce_accounts = icepick_module::help::Operation {
|
||||
name: "find-nonce-accounts".to_string(),
|
||||
description: "Find all nonce accounts for an authorized address".to_string(),
|
||||
arguments: vec![
|
||||
cluster.clone(),
|
||||
Argument {
|
||||
name: "authorization_address".to_string(),
|
||||
description: "The account authorized to use and advance nonces.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let transfer = icepick_module::help::Operation {
|
||||
name: "transfer".to_string(),
|
||||
description: "Transfer SOL from a Keyfork wallet to an external wallet.".to_string(),
|
||||
arguments: vec![
|
||||
Argument {
|
||||
name: "amount".to_string(),
|
||||
description: "The amount of SOL to transfer.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
account.clone(),
|
||||
Argument {
|
||||
name: "to_address".to_string(),
|
||||
description: "The address to send SOL to.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
from_address.clone(),
|
||||
fee.clone(),
|
||||
fee_payer.clone(),
|
||||
fee_payer_address.clone(),
|
||||
],
|
||||
};
|
||||
let stake = icepick_module::help::Operation {
|
||||
name: "stake".to_string(),
|
||||
description: "Stake SOL to earn rewards.".to_string(),
|
||||
arguments: vec![
|
||||
Argument {
|
||||
name: "amount".to_string(),
|
||||
description: "The amount of SOL to stake.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
account.clone(),
|
||||
from_address.clone(),
|
||||
fee.clone(),
|
||||
fee_payer.clone(),
|
||||
fee_payer_address.clone(),
|
||||
],
|
||||
};
|
||||
// kinda BS that you have to make an account for a token, but ok.
|
||||
let create_token_account = icepick_module::help::Operation {
|
||||
name: "create-token-account".to_string(),
|
||||
description: "Create an account for a given token".to_string(),
|
||||
arguments: vec![
|
||||
Argument {
|
||||
name: "wallet_address".to_string(),
|
||||
description: "The address of the token.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
Argument {
|
||||
name: "token_address".to_string(),
|
||||
description: "The address of the token.".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
},
|
||||
Argument {
|
||||
name: "funder_address".to_string(),
|
||||
description: "The address of the funder (signer).".to_string(),
|
||||
r#type: ArgumentType::Optional,
|
||||
},
|
||||
],
|
||||
};
|
||||
let transfer_token = icepick_module::help::Operation {
|
||||
name: "transfer-token".to_string(),
|
||||
description: "Transfer tokens from a Keyfork wallet to an external wallet."
|
||||
.to_string(),
|
||||
|
@ -455,21 +505,61 @@ impl Module for Solana {
|
|||
fee_payer.clone(),
|
||||
fee_payer_address.clone(),
|
||||
],
|
||||
},
|
||||
icepick_module::help::Operation {
|
||||
name: "sign".to_string(),
|
||||
description: "Sign a previously-generated transaction.".to_string(),
|
||||
arguments: vec![Argument {
|
||||
name: "blockhash".to_string(),
|
||||
description: "A recent blockhash".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
}],
|
||||
},
|
||||
icepick_module::help::Operation {
|
||||
name: "broadcast".to_string(),
|
||||
description: "Broadcast a signed transaction".to_string(),
|
||||
arguments: vec![cluster.clone()],
|
||||
},
|
||||
};
|
||||
let compile = icepick_module::help::Operation {
|
||||
name: "compile".to_string(),
|
||||
description: "Compile instructions into a transaction".to_string(),
|
||||
arguments: vec![
|
||||
Argument {
|
||||
name: "blockhash".to_string(),
|
||||
description: "A recent blockhash, must be provided in place of nonce"
|
||||
.to_string(),
|
||||
r#type: ArgumentType::Optional,
|
||||
},
|
||||
Argument {
|
||||
name: "nonce".to_string(),
|
||||
description: "A durable nonce, must be provided in place of blockhash"
|
||||
.to_string(),
|
||||
r#type: ArgumentType::Optional,
|
||||
},
|
||||
],
|
||||
};
|
||||
let inspect = icepick_module::help::Operation {
|
||||
name: "inspect".to_string(),
|
||||
description: "Print a transaction using base64.".to_string(),
|
||||
arguments: vec![],
|
||||
};
|
||||
let sign = icepick_module::help::Operation {
|
||||
name: "sign".to_string(),
|
||||
description: "Sign a previously-generated transaction.".to_string(),
|
||||
arguments: vec![Argument {
|
||||
name: "blockhash".to_string(),
|
||||
description: "A recent blockhash".to_string(),
|
||||
r#type: ArgumentType::Required,
|
||||
}],
|
||||
};
|
||||
let broadcast = icepick_module::help::Operation {
|
||||
name: "broadcast".to_string(),
|
||||
description: "Broadcast a signed transaction".to_string(),
|
||||
arguments: vec![cluster.clone()],
|
||||
};
|
||||
vec![
|
||||
get_blockhash,
|
||||
generate_wallet,
|
||||
get_wallet_address,
|
||||
await_funds,
|
||||
get_token_info,
|
||||
create_nonce_account_and_signing_key,
|
||||
get_nonce_account_data,
|
||||
find_nonce_accounts,
|
||||
transfer,
|
||||
stake,
|
||||
create_token_account,
|
||||
transfer_token,
|
||||
compile,
|
||||
inspect,
|
||||
sign,
|
||||
broadcast,
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -594,6 +684,10 @@ impl Module for Solana {
|
|||
let from_pk = Pubkey::from_str(&from_address).unwrap();
|
||||
let authorization_pk = Pubkey::from_str(&authorization_address).unwrap();
|
||||
|
||||
if from_account.is_some() {
|
||||
unimplemented!("alternative derivation accounts are not yet implemented");
|
||||
}
|
||||
|
||||
let instructions = system_instruction::create_nonce_account(
|
||||
&from_pk,
|
||||
&keypair.pubkey(),
|
||||
|
@ -602,20 +696,35 @@ impl Module for Solana {
|
|||
1500000,
|
||||
);
|
||||
|
||||
let message = solana_sdk::message::Message::new(&instructions, None);
|
||||
let transaction = solana_sdk::transaction::Transaction::new_unsigned(message);
|
||||
let from_account = from_account
|
||||
.and_then(|a| u32::from_str(&a).ok())
|
||||
.unwrap_or(0);
|
||||
let requested_accounts = vec![from_account | 1 << 31];
|
||||
|
||||
#[allow(clippy::identity_op)]
|
||||
Ok(serde_json::json!({
|
||||
"blob": {
|
||||
"nonce_pubkey": keypair.pubkey().to_string(),
|
||||
"nonce_privkey": [keypair.secret().to_bytes()],
|
||||
"transaction": transaction,
|
||||
"transaction": instructions,
|
||||
},
|
||||
"derivation_accounts": [0u32 | 1 << 31],
|
||||
}))
|
||||
}
|
||||
Operation::GetNonceAccountData(GetNonceAccountData {
|
||||
nonce_address,
|
||||
cluster,
|
||||
}) => {
|
||||
let nonce_pk = Pubkey::from_str(&nonce_address).unwrap();
|
||||
let cluster = cluster.unwrap_or(Cluster::MainnetBeta);
|
||||
let cluster_url = format!("https://api.{cluster}.solana.com");
|
||||
let client = solana_rpc_client::rpc_client::RpcClient::new(cluster_url);
|
||||
|
||||
let nonce_account = client.get_account(&nonce_pk).unwrap();
|
||||
let nonce =
|
||||
solana_rpc_client_nonce_utils::data_from_account(&nonce_account).unwrap();
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"blob": {
|
||||
"authority": nonce.authority.to_string(),
|
||||
"durable_nonce": nonce.durable_nonce.as_hash().to_string(),
|
||||
"lamports_per_signature": nonce.fee_calculator.lamports_per_signature,
|
||||
},
|
||||
"derivation_accounts": requested_accounts,
|
||||
}))
|
||||
}
|
||||
Operation::FindNonceAccounts(FindNonceAccounts {
|
||||
|
@ -749,56 +858,30 @@ impl Module for Solana {
|
|||
from_account,
|
||||
to_address,
|
||||
from_address,
|
||||
fee: _,
|
||||
fee,
|
||||
fee_payer,
|
||||
fee_payer_address,
|
||||
}) => {
|
||||
// TODO:
|
||||
// parse address for to_address
|
||||
if from_account.is_some() {
|
||||
unimplemented!("from_account");
|
||||
}
|
||||
if fee.is_some() | fee_payer.is_some() | fee_payer_address.is_some() {
|
||||
unimplemented!("fee")
|
||||
}
|
||||
|
||||
let amount = f64::from_str(&amount).expect("float amount");
|
||||
let amount: u64 = (amount * LAMPORTS_PER_SOL as f64) as u64;
|
||||
|
||||
let to_pk = Pubkey::from_str(&to_address).unwrap();
|
||||
let from_pk = Pubkey::from_str(&from_address).unwrap();
|
||||
let payer_account_and_pk = {
|
||||
// If a fee payer is given, a fee payer address must also be given, since the
|
||||
// address must be known before signing the transaction.
|
||||
match (&fee_payer, &fee_payer_address) {
|
||||
(Some(payer), Some(address)) => {
|
||||
// Use the provided account
|
||||
Some((
|
||||
u32::from_str(payer).unwrap(),
|
||||
Pubkey::from_str(address).unwrap(),
|
||||
))
|
||||
}
|
||||
(None, None) => {
|
||||
// Use the transaction account
|
||||
None
|
||||
}
|
||||
_ => panic!("Invalid combination of fee_payer and fee_payer_address"),
|
||||
}
|
||||
};
|
||||
let instruction = system_instruction::transfer(&from_pk, &to_pk, amount);
|
||||
let message = solana_sdk::message::Message::new(
|
||||
&[instruction],
|
||||
payer_account_and_pk.map(|v| v.1).as_ref(),
|
||||
);
|
||||
let transaction = solana_sdk::transaction::Transaction::new_unsigned(message);
|
||||
// TODO: error handling from_str
|
||||
let from_account = from_account
|
||||
.and_then(|a| u32::from_str(&a).ok())
|
||||
.unwrap_or(0);
|
||||
let mut requested_accounts = vec![];
|
||||
requested_accounts.push(from_account | 1 << 31);
|
||||
if let Some((account, _)) = &payer_account_and_pk {
|
||||
requested_accounts.push(*account | 1 << 31);
|
||||
}
|
||||
#[allow(clippy::identity_op)]
|
||||
Ok(serde_json::json!({
|
||||
"blob": {
|
||||
"transaction": transaction,
|
||||
"instructions": [instruction],
|
||||
// This is done in blob since it's compiled in the next step
|
||||
"derivation_accounts": [0u32 | 1 << 31],
|
||||
},
|
||||
"derivation_accounts": requested_accounts,
|
||||
}))
|
||||
}
|
||||
Operation::CreateTokenAccount(CreateTokenAccount {
|
||||
|
@ -843,39 +926,23 @@ impl Module for Solana {
|
|||
fee_payer,
|
||||
fee_payer_address,
|
||||
}) => {
|
||||
// TODO: deduplicate code used in Transfer
|
||||
if from_account.is_some() {
|
||||
unimplemented!("from_account");
|
||||
}
|
||||
if fee.is_some() | fee_payer.is_some() | fee_payer_address.is_some() {
|
||||
unimplemented!("fee")
|
||||
}
|
||||
|
||||
let amount = f64::from_str(&amount).expect("float amount");
|
||||
let decimals = u8::from_str(&decimals).expect("decimals");
|
||||
let amount: u64 = (amount * 10u64.pow(decimals as u32) as f64) as u64;
|
||||
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use spl_associated_token_account::get_associated_token_address;
|
||||
let to_pk = Pubkey::from_str(&to_address).unwrap();
|
||||
let from_pk = Pubkey::from_str(&from_address).unwrap();
|
||||
let token_pk = Pubkey::from_str(&token_address).unwrap();
|
||||
let payer_account_and_pk = {
|
||||
// If a fee payer is given, a fee payer address must also be given, since the
|
||||
// address must be known before signing the transaction.
|
||||
match (&fee_payer, &fee_payer_address) {
|
||||
(Some(payer), Some(address)) => {
|
||||
// Use the provided account
|
||||
Some((
|
||||
u32::from_str(payer).unwrap(),
|
||||
Pubkey::from_str(address).unwrap(),
|
||||
))
|
||||
}
|
||||
(None, None) => {
|
||||
// Use the transaction account
|
||||
None
|
||||
}
|
||||
_ => panic!("Invalid combination of fee_payer and fee_payer_address"),
|
||||
}
|
||||
};
|
||||
let token_program_id = spl_token::ID;
|
||||
let mut signers = vec![&from_pk];
|
||||
if let Some((_, pk)) = payer_account_and_pk.as_ref() {
|
||||
signers.push(pk);
|
||||
}
|
||||
|
||||
let from_token_address = get_associated_token_address(&from_pk, &token_pk);
|
||||
let to_token_address = get_associated_token_address(&to_pk, &token_pk);
|
||||
|
@ -892,36 +959,75 @@ impl Module for Solana {
|
|||
decimals, // decimals
|
||||
)
|
||||
.unwrap();
|
||||
// TODO: check if this works with payer
|
||||
|
||||
// TODO: check if this works with multisig
|
||||
// this is required because the Solana SDK does not set the primary transactional
|
||||
// key as writable (the one that would be paying computation fees) in the event a
|
||||
// payer is not provided. The transactional account must be writable for the
|
||||
// computation fee to be paid.
|
||||
if payer_account_and_pk.is_none() {
|
||||
for account in instruction.accounts.iter_mut() {
|
||||
if account.pubkey == from_pk {
|
||||
account.is_writable = true;
|
||||
}
|
||||
for account in instruction.accounts.iter_mut() {
|
||||
if account.pubkey == from_pk {
|
||||
account.is_writable = true;
|
||||
}
|
||||
}
|
||||
let message = solana_sdk::message::Message::new(
|
||||
&[instruction],
|
||||
payer_account_and_pk.map(|v| v.1).as_ref(),
|
||||
);
|
||||
// message.header.num_readonly_signed_accounts = 0;
|
||||
let transaction =
|
||||
solana_sdk::transaction::Transaction::new_unsigned(message.clone());
|
||||
/*
|
||||
use base64::prelude::*;
|
||||
eprintln!("{}", BASE64_STANDARD.encode(transaction.message_data()));
|
||||
*/
|
||||
|
||||
#[allow(clippy::identity_op)]
|
||||
Ok(serde_json::json!({
|
||||
"blob": {
|
||||
"instructions": [instruction],
|
||||
"derivation_accounts": [0u32 | 1 << 31],
|
||||
},
|
||||
}))
|
||||
}
|
||||
Operation::Compile(Compile {
|
||||
hashable,
|
||||
derivation_accounts,
|
||||
mut instructions,
|
||||
}) => {
|
||||
use solana_sdk::{hash::Hash, message::Message, transaction::Transaction};
|
||||
|
||||
let (hash, transaction) = match hashable {
|
||||
// We already have the account from GetNonceAccountData,
|
||||
// which also gives us the authority and the nonce itself.
|
||||
Hashable::Nonce {
|
||||
nonce_data,
|
||||
nonce_address,
|
||||
nonce_authority,
|
||||
} => {
|
||||
let account_pk = Pubkey::from_str(&nonce_address).unwrap();
|
||||
let authority_pk = Pubkey::from_str(&nonce_authority).unwrap();
|
||||
|
||||
let hash = Hash::from_str(&nonce_data).unwrap();
|
||||
let increment_nonce =
|
||||
system_instruction::advance_nonce_account(&account_pk, &authority_pk);
|
||||
|
||||
instructions.insert(0, increment_nonce);
|
||||
let message = Message::new(&instructions, None);
|
||||
let transaction = Transaction::new_unsigned(message);
|
||||
(hash, transaction)
|
||||
}
|
||||
Hashable::Blockhash { blockhash } => {
|
||||
let blockhash = Hash::from_str(&blockhash).unwrap();
|
||||
let message = Message::new(&instructions, None);
|
||||
let transaction = Transaction::new_unsigned(message);
|
||||
(blockhash, transaction)
|
||||
}
|
||||
};
|
||||
Ok(serde_json::json!({
|
||||
"blob": {
|
||||
"hash": hash,
|
||||
"transaction": transaction,
|
||||
},
|
||||
"derivation_accounts": [0u32 | 1 << 31],
|
||||
"derivation_accounts": derivation_accounts,
|
||||
}))
|
||||
}
|
||||
Operation::Inspect(Inspect { transaction }) => {
|
||||
use base64::prelude::*;
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"blob": {
|
||||
"formatted_transaction": BASE64_STANDARD.encode(transaction.message_data())
|
||||
}
|
||||
}))
|
||||
}
|
||||
Operation::Sign(Sign {
|
||||
|
|
Loading…
Reference in New Issue