Gửi Transaction
Làm thế nào để gửi SOL
Để gửi SOL, bạn sẽ cần tương tác với SystemProgram.
import {
Connection,
Keypair,
SystemProgram,
LAMPORTS_PER_SOL,
Transaction,
sendAndConfirmTransaction,
} from "@solana/web3.js";
(async () => {
const fromKeypair = Keypair.generate();
const toKeypair = Keypair.generate();
const connection = new Connection(
"https://api.devnet.solana.com",
"confirmed"
);
const airdropSignature = await connection.requestAirdrop(
fromKeypair.publicKey,
LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature);
const lamportsToSend = 1_000_000;
const transferTransaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: fromKeypair.publicKey,
toPubkey: toKeypair.publicKey,
lamports: lamportsToSend,
})
);
await sendAndConfirmTransaction(connection, transferTransaction, [
fromKeypair,
]);
})();
const transferTransaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: fromKeypair.publicKey,
toPubkey: toKeypair.publicKey,
lamports: lamportsToSend,
})
);
await sendAndConfirmTransaction(connection, transferTransaction, [fromKeypair]);
from solana.rpc.api import Client
from solders.keypair import Keypair
from solana.transaction import Transaction
from solders.system_program import TransferParams, transfer
LAMPORT_PER_SOL = 1000000000
client: Client = Client("https://api.devnet.solana.com")
sender = Keypair()
receiver = Keypair()
airdrop = client.request_airdrop(sender.pubkey(), 1 * LAMPORT_PER_SOL)
airdrop_signature = airdrop.value
client.confirm_transaction(airdrop_signature)
transaction = Transaction().add(transfer(TransferParams(
from_pubkey=sender.pubkey(),
to_pubkey=receiver.pubkey(),
lamports=1_000_000)
))
client.send_transaction(transaction, sender)
transaction = Transaction().add(transfer(TransferParams(
from_pubkey=sender.pubkey(),
to_pubkey=receiver.pubkey(),
lamports=1_000_000)
))
client.send_transaction(transaction, sender)
import { WalletNotConnectedError } from "@solana/wallet-adapter-base";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import { Keypair, SystemProgram, Transaction } from "@solana/web3.js";
import React, { FC, useCallback } from "react";
export const SendTenLamportToRandomAddress: FC = () => {
const { connection } = useConnection();
const { publicKey, sendTransaction } = useWallet();
const onClick = useCallback(async () => {
if (!publicKey) throw new WalletNotConnectedError();
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: Keypair.generate().publicKey,
lamports: 1_000_000,
})
);
const signature = await sendTransaction(transaction, connection);
await connection.confirmTransaction(signature, "processed");
}, [publicKey, sendTransaction, connection]);
return (
<button onClick={onClick} disabled={!publicKey}>
Send 1 lamport to a random address!
</button>
);
};
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: Keypair.generate().publicKey,
lamports: 1_000_000,
})
);
const signature = await sendTransaction(transaction, connection);
await connection.confirmTransaction(signature, "processed");
use solana_client::rpc_client::RpcClient;
use solana_program::system_instruction;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::native_token::LAMPORTS_PER_SOL;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::transaction::Transaction;
fn main() {
let from = Keypair::new();
let frompubkey = Signer::pubkey(&from);
let to = Keypair::new();
let topubkey = Signer::pubkey(&to);
let lamports_to_send = 1_000_000;
let rpc_url = String::from("https://api.devnet.solana.com");
let connection = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());
///Airdropping some Sol to the 'from' account
match connection.request_airdrop(&frompubkey, LAMPORTS_PER_SOL) {
Ok(sig) => loop {
if let Ok(confirmed) = connection.confirm_transaction(&sig) {
if confirmed {
println!("Transaction: {} Status: {}", sig, confirmed);
break;
}
}
},
Err(_) => println!("Error requesting airdrop"),
};
///Creating the transfer sol instruction
let ix = system_instruction::transfer(&frompubkey, &topubkey, lamports_to_send);
///Putting the transfer sol instruction into a transaction
let recent_blockhash = connection.get_latest_blockhash().expect("Failed to get latest blockhash.");
let txn = Transaction::new_signed_with_payer(&[ix], Some(&frompubkey), &[&from], recent_blockhash);
///Sending the transfer sol transaction
match connection.send_and_confirm_transaction(&txn){
Ok(sig) => loop {
if let Ok(confirmed) = connection.confirm_transaction(&sig) {
if confirmed {
println!("Transaction: {} Status: {}", sig, confirmed);
break;
}
}
},
Err(e) => println!("Error transferring Sol:, {}", e),
}
}
system_instruction::transfer(&from, &to, lamports_to_send);
solana transfer --from <KEYPAIR> <RECIPIENT_ACCOUNT_ADDRESS> 0.001 --allow-unfunded-recipient --url https://api.devnet.solana.com --fee-payer <KEYPAIR>
solana transfer --from <KEYPAIR> <RECIPIENT_ACCOUNT_ADDRESS> 0.001 --allow-unfunded-recipient --url https://api.devnet.solana.com --fee-payer <KEYPAIR>
Làm thế nào để gửi SPL-Tokens
Sử dụng Token Program để gửi SPL Tokens. Để gửi SPL token, bạn cần biết địa chỉ token account tương ứng SPL Token được gửi. Bạ có thể biết được địa chỉ gửi và nhận bằng cí dụ dưới đây.
const web3 = require("@solana/web3.js");
const splToken = require("@solana/spl-token");
(async () => {
// Connect to cluster
const connection = new web3.Connection(
web3.clusterApiUrl("devnet"),
"confirmed"
);
// Generate a new wallet keypair and airdrop SOL
const fromWallet = web3.Keypair.generate();
const fromAirdropSignature = await connection.requestAirdrop(
fromWallet.publicKey,
web3.LAMPORTS_PER_SOL
);
// Wait for airdrop confirmation
await connection.confirmTransaction(fromAirdropSignature);
// Generate a new wallet to receive newly minted token
const toWallet = web3.Keypair.generate();
// Create new token mint
const mint = await splToken.createMint(
connection,
fromWallet,
fromWallet.publicKey,
null,
9,
splToken.TOKEN_PROGRAM_ID
);
// Get the token account of the fromWallet Solana address, if it does not exist, create it
const fromTokenAccount = await mint.getOrCreateAssociatedAccountInfo(
fromWallet.publicKey
);
//get the token account of the toWallet Solana address, if it does not exist, create it
const toTokenAccount = await mint.getOrCreateAssociatedAccountInfo(
toWallet.publicKey
);
// Minting 1 new token to the "fromTokenAccount" account we just returned/created
await mint.mintTo(
fromTokenAccount.address,
fromWallet.publicKey,
[],
1000000000 // it's 1 token, but in lamports
);
// Add token transfer instructions to transaction
const transaction = new web3.Transaction().add(
splToken.createTransferInstruction(
splToken.TOKEN_PROGRAM_ID,
fromTokenAccount.address,
toTokenAccount.address,
fromWallet.publicKey,
[],
1
)
);
// Sign transaction, broadcast, and confirm
await web3.sendAndConfirmTransaction(connection, transaction, [fromWallet]);
})();
// Add token transfer instructions to transaction
const transaction = new web3.Transaction().add(
splToken.Token.createTransferInstruction(
splToken.TOKEN_PROGRAM_ID,
fromTokenAccount.address,
toTokenAccount.address,
fromWallet.publicKey,
[],
1
)
);
// Sign transaction, broadcast, and confirm
await web3.sendAndConfirmTransaction(connection, transaction, [fromWallet]);
import { WalletNotConnectedError } from "@solana/wallet-adapter-base";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import { Keypair, SystemProgram, Transaction } from "@solana/web3.js";
import { TOKEN_PROGRAM_ID, createTransferInstruction } from "@solana/spl-token";
import React, { FC, useCallback } from "react";
export const SendSPLTokenToAddress: FC = (
fromTokenAccount,
toTokenAccount,
fromWallet
) => {
const { connection } = useConnection();
const { publicKey, sendTransaction } = useWallet();
const onClick = useCallback(async () => {
if (!publicKey) throw new WalletNotConnectedError();
const transaction = new Transaction().add(
createTransferInstruction(
fromTokenAccount.address,
toTokenAccount.address,
fromWallet.publicKey,
1,
[],
TOKEN_PROGRAM_ID
)
);
const signature = await sendTransaction(transaction, connection);
await connection.confirmTransaction(signature, "processed");
}, [publicKey, sendTransaction, connection]);
return (
<button onClick={onClick} disabled={!publicKey}>
Send 1 lamport to a random address!
</button>
);
};
const transaction = new Transaction().add(
Token.createTransferInstruction(
TOKEN_PROGRAM_ID,
fromTokenAccount.address,
toTokenAccount.address,
fromWallet.publicKey,
[],
1
)
);
const signature = await sendTransaction(transaction, connection);
await connection.confirmTransaction(signature, "processed");
$ spl-token transfer AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 50 vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg
Transfer 50 tokens
Sender: 7UX2i7SucgLMQcfZ75s3VXmZZY4YRUyJN9X1RgfMoDUi
Recipient: vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg
Recipient associated token account: F59618aQB8r6asXeMcB9jWuY6NEx1VduT9yFo1GTi1ks
Signature: 5a3qbvoJQnTAxGPHCugibZTbSu7xuTgkxvF4EJupRjRXGgZZrnWFmKzfEzcqKF2ogCaF4QKVbAtuFx7xGwrDUcGd
$ spl-token transfer AQoKYV7tYpTrFZN6P5oUufbQKAUr9mNYGe1TTJC9wajM 50 vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg
Transfer 50 tokens
Sender: 7UX2i7SucgLMQcfZ75s3VXmZZY4YRUyJN9X1RgfMoDUi
Recipient: vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg
Recipient associated token account: F59618aQB8r6asXeMcB9jWuY6NEx1VduT9yFo1GTi1ks
Signature: 5a3qbvoJQnTAxGPHCugibZTbSu7xuTgkxvF4EJupRjRXGgZZrnWFmKzfEzcqKF2ogCaF4QKVbAtuFx7xGwrDUcGd
Làm thế nào để tính chi phí gửi transaction
Số lượng chữ ký bên trong một transaction sẽ phản ánh chi phí cho transaction đó. CHỉ cần bạn không tạo mới account, đó cũng chính là chi phí phải trả cho transaction đó. Trong trường hợp phát sinh chi phí thuê, tham khảo tính toán phí thuê.
Hai ví dụ sau đây sẽ trình bày 2 cách hiện hành để ước tính chi phí của một transaction.
Ví dụ đầu tiên sử dụng getEstimatedFee
, là một phương thức mới được thêm vào lớp Transaction
, trong khi ví dụ 2 sử dụng getFeeForMessage
, là phương thức thay thế cho getFeeCalculatorForBlockhash
trong lớp Connection
.
getEstimatedFee
import {
clusterApiUrl,
Connection,
Keypair,
SystemProgram,
Transaction,
} from "@solana/web3.js";
(async () => {
// Connect to cluster
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const payer = Keypair.generate();
const payee = Keypair.generate();
const recentBlockhash = await connection.getLatestBlockhash();
const transaction = new Transaction({
recentBlockhash: recentBlockhash.blockhash,
feePayer: payer.publicKey
}).add(
SystemProgram.transfer({
fromPubkey: payer.publicKey,
toPubkey: payee.publicKey,
lamports: 10,
})
);
const fees = await transaction.getEstimatedFee(connection);
console.log(`Estimated SOL transfer cost: ${fees} lamports`);
// Estimated SOL transfer cost: 5000 lamports
})();
const recentBlockhash = await connection.getLatestBlockhash();
const transaction = new Transaction({
recentBlockhash: recentBlockhash.blockhash,
}).add(
SystemProgram.transfer({
fromPubkey: payer.publicKey,
toPubkey: payee.publicKey,
lamports: 10,
})
);
const fees = await transaction.getEstimatedFee(connection);
console.log(`Estimated SOL transfer cost: ${fees} lamports`);
// Estimated SOL transfer cost: 5000 lamports
getFeeForMessage
import {
clusterApiUrl,
Connection,
Keypair,
Message,
SystemProgram,
SYSTEM_INSTRUCTION_LAYOUTS,
Transaction,
} from "@solana/web3.js";
import bs58 from "bs58";
(async () => {
// Connect to cluster
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const payer = Keypair.generate();
const payee = Keypair.generate();
const type = SYSTEM_INSTRUCTION_LAYOUTS.Transfer;
const data = Buffer.alloc(type.layout.span);
const layoutFields = Object.assign({ instruction: type.index });
type.layout.encode(layoutFields, data);
const recentBlockhash = await connection.getLatestBlockhash();
const messageParams = {
accountKeys: [
payer.publicKey.toString(),
payee.publicKey.toString(),
SystemProgram.programId.toString(),
],
header: {
numReadonlySignedAccounts: 0,
numReadonlyUnsignedAccounts: 1,
numRequiredSignatures: 1,
},
instructions: [
{
accounts: [0, 1],
data: bs58.encode(data),
programIdIndex: 2,
},
],
recentBlockhash: recentBlockhash.blockhash,
};
const message = new Message(messageParams);
const fees = await connection.getFeeForMessage(message);
console.log(`Estimated SOL transfer cost: ${fees.value} lamports`);
// Estimated SOL transfer cost: 5000 lamports
})();
const message = new Message(messageParams);
const fees = await connection.getFeeForMessage(message);
console.log(`Estimated SOL transfer cost: ${fees.value} lamports`);
// Estimated SOL transfer cost: 5000 lamports
Làm thế nào để thêm ghi chú và một transaction
Bất kỳ transaction nào cũng có thể được thêm một ghi chú thông qua memo program. Hiện tại, programID
của Memo Program có thể thêm thủ công bằng địa chỉ MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr
.
import {
Connection,
Keypair,
SystemProgram,
LAMPORTS_PER_SOL,
PublicKey,
Transaction,
TransactionInstruction,
sendAndConfirmTransaction,
} from "@solana/web3.js";
(async () => {
const fromKeypair = Keypair.generate();
const toKeypair = Keypair.generate();
const connection = new Connection(
"https://api.devnet.solana.com",
"confirmed"
);
const airdropSignature = await connection.requestAirdrop(
fromKeypair.publicKey,
LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature);
const lamportsToSend = 10;
const transferTransaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: fromKeypair.publicKey,
toPubkey: toKeypair.publicKey,
lamports: lamportsToSend,
})
);
await transferTransaction.add(
new TransactionInstruction({
keys: [
{ pubkey: fromKeypair.publicKey, isSigner: true, isWritable: true },
],
data: Buffer.from("Data to send in transaction", "utf-8"),
programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
})
);
await sendAndConfirmTransaction(connection, transferTransaction, [
fromKeypair,
]);
})();
const transferTransaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: fromKeypair.publicKey,
toPubkey: toKeypair.publicKey,
lamports: lamportsToSend,
})
);
await transferTransaction.add(
new TransactionInstruction({
keys: [{ pubkey: fromKeypair.publicKey, isSigner: true, isWritable: true }],
data: Buffer.from("Data to send in transaction", "utf-8"),
programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
})
);
await sendAndConfirmTransaction(connection, transferTransaction, [fromKeypair]);
import { WalletNotConnectedError } from "@solana/wallet-adapter-base";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import {
Keypair,
PublicKey,
SystemProgram,
Transaction,
TransactionInstruction,
} from "@solana/web3.js";
import React, { FC, useCallback } from "react";
export const SendTenLamportToRandomAddress: FC = () => {
const { connection } = useConnection();
const { publicKey, sendTransaction } = useWallet();
const onClick = useCallback(async () => {
if (!publicKey) throw new WalletNotConnectedError();
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: Keypair.generate().publicKey,
lamports: 10,
})
);
await transaction.add(
new TransactionInstruction({
keys: [{ pubkey: publicKey, isSigner: true, isWritable: true }],
data: Buffer.from("Data to send in transaction", "utf-8"),
programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
})
);
const signature = await sendTransaction(transaction, connection);
await connection.confirmTransaction(signature, "processed");
}, [publicKey, sendTransaction, connection]);
return (
<button onClick={onClick} disabled={!publicKey}>
Send 1 lamport to a random address!
</button>
);
};
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: publicKey,
toPubkey: Keypair.generate().publicKey,
lamports: 10,
})
);
await transaction.add(
new TransactionInstruction({
keys: [{ pubkey: publicKey, isSigner: true, isWritable: true }],
data: Buffer.from("Data to send in transaction", "utf-8"),
programId: new PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
})
);
const signature = await sendTransaction(transaction, connection);
await connection.confirmTransaction(signature, "processed");
solana transfer --from <KEYPAIR> <RECIPIENT_ACCOUNT_ADDRESS> 0.5 --allow-unfunded-recipient --url https://api.devnet.solana.com --fee-payer <KEYPAIR> --with-memo <MEMO>
solana transfer --from <KEYPAIR> <RECIPIENT_ACCOUNT_ADDRESS> 0.5 --allow-unfunded-recipient --url https://api.devnet.solana.com --fee-payer <KEYPAIR> --with-memo <MEMO>
Làm thế nào để thay đổi ngân sách tính toán cho một transaction
Ngân sách tính toán cho một transaction có thể thay đổi được bằng cách thêm vào một chỉ thị gọi đến Compute Budget Program. Mặc định, ngân sách tính toán được thiết lập ở mức 200k đơn vị tính toán nhân với số lượng chỉ thị và không vượt quá 1.4M đơn vị tính toán. Càng ít bước tính toán, chi phí bỏ ra sẽ càng ít.
Lưu ý: Để thay đổi ngân sách tính toán, bạn phải chắc rằng chỉ thị đầu tiên trong 3 chỉ thị của transaction là thiết lập ngân sách.
import { BN } from "@project-OpenBook/anchor";
import {
Keypair,
Connection,
LAMPORTS_PER_SOL,
sendAndConfirmTransaction,
ComputeBudgetProgram,
SystemProgram,
Transaction,
} from "@solana/web3.js";
(async () => {
const payer = Keypair.generate();
const toAccount = Keypair.generate().publicKey;
const connection = new Connection("http://127.0.0.1:8899", "confirmed");
const airdropSignature = await connection.requestAirdrop(
payer.publicKey,
LAMPORTS_PER_SOL
);
await connection.confirmTransaction(airdropSignature);
const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
units: 1000000
});
const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 1
});
// Total fee will be 5,001 Lamports for 1M CU
const transaction = new Transaction()
.add(modifyComputeUnits)
.add(addPriorityFee)
.add(
SystemProgram.transfer({
fromPubkey: payer.publicKey,
toPubkey: toAccount,
lamports: 10000000,
})
);
const signature = await sendAndConfirmTransaction(connection, transaction, [
payer,
]);
console.log(signature);
const result = await connection.getParsedTransaction(signature);
console.log(result);
})();
const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
units: 1000000
});
const addPriorityFee = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 1
});
const transaction = new Transaction()
.add(modifyComputeUnits)
.add(addPriorityFee)
.add(
SystemProgram.transfer({
fromPubkey: payer.publicKey,
toPubkey: toAccount,
lamports: 10000000,
})
);
//! @brief Example Budget Management
use solana_client::rpc_client::RpcClient;
use solana_program::{instruction::Instruction, message::Message, pubkey::Pubkey};
use solana_sdk::{
compute_budget::ComputeBudgetInstruction,
pubkey,
signature::{Keypair, Signature},
signer::Signer,
transaction::Transaction,
};
/// Submits the program instruction as per the instruction definition
fn submit_transaction(
rpc_client: &RpcClient,
wallet_signer: &dyn Signer,
instructions: Vec<Instruction>,
) -> Result<Signature, Box<dyn std::error::Error>> {
let mut transaction =
Transaction::new_unsigned(Message::new(&instructions, Some(&wallet_signer.pubkey())));
let recent_blockhash = rpc_client
.get_latest_blockhash()
.map_err(|err| format!("error: unable to get recent blockhash: {}", err))?;
transaction
.try_sign(&vec![wallet_signer], recent_blockhash)
.map_err(|err| format!("error: failed to sign transaction: {}", err))?;
let signature = rpc_client
.send_and_confirm_transaction(&transaction)
.map_err(|err| format!("error: send transaction: {}", err))?;
Ok(signature)
}
const PROG_KEY: Pubkey = pubkey!("PWDnx8LkjJUn9bAVzG6Fp6BuvB41x7DkBZdo9YLMGcc");
/// Increase the Transaction Budget and call normal instruction(s)
/// Here we send redundant transactions to witness Compute Budget drawdown
fn send_instructions_demo(
rpc_client: &RpcClient,
wallet_signer: &dyn Signer,
) -> Result<(), Box<dyn std::error::Error>> {{
let accounts = &[];
let txn = submit_transaction(
&connection,
&wallet_signer,
// Array of instructions: 0: Set Compute Unit Limt, 1: Set Prioritization Fee,
// 2: Do something, 3: Do something else
[ComputeBudgetInstruction::set_compute_unit_limit(1_000_000u32),
ComputeBudgetInstruction::set_compute_unit_price(1u32),
Instruction::new_with_borsh(PROG_KEY, &0u8, accounts.to_vec()),
Instruction::new_with_borsh(PROG_KEY, &0u8, accounts.to_vec())].to_vec(),
)?;
println!("{:?}", txn);
Ok(())
}
let txn = submit_transaction(
&connection,
&wallet_signer,
// Array of instructions: 0: Set Compute Unit Limt, 1: Set Prioritization Fee,
// 2: Do something, 3: Do something else
[ComputeBudgetInstruction::set_compute_unit_limit(1_000_000u32),
ComputeBudgetInstruction::set_compute_unit_price(1u32),
Instruction::new_with_borsh(PROG_KEY, &0u8, accounts.to_vec()),
Instruction::new_with_borsh(PROG_KEY, &0u8, accounts.to_vec())].to_vec(),
)?;
Ví dụ kết quả in ra của Program:
[ 1] Program ComputeBudget111111111111111111111111111111 invoke [1]
[ 2] Program ComputeBudget111111111111111111111111111111 success
[ 3]
[ 4] Program ComputeBudget111111111111111111111111111111 invoke [1]
[ 5] Program ComputeBudget111111111111111111111111111111 success