Mab*_*Oza 4 rust blockchain solana solana-cli anchor-solana
我想将sol 空投到特定帐户中,以使我的智能合约触发锚定测试。每当我运行测试时,我都会遇到错误Error: Account BdFchxtKbEaWtsbuFX2nbKyZCjhz5LN8vyPukQzJQrME has insufficient funds for spend (1.30250136 SOL) + fee (0.000945 SOL) There was a problem deploying: Output { status: ExitStatus(ExitStatus(256)), stdout: "", stderr: "" }.
该错误是不言自明的,但如何将代币空投到该特定帐户,我可以运行solana airdrop 5
,但随后它将其空投到另一个帐户 GgRbD2AdibdAtBrQMJJEooco9gKt48Sd98Gn7vZQ1nJK。
我什至尝试使用将代币从一个帐户转移到另一个帐户solana transfer BdFchxtKbEaWtsbuFX2nbKyZCjhz5LN8vyPukQzJQrME 5 --allow-unfunded-recipient
,但交易只是挂起。
下面是我使用 Anchor 在 Rust 中编写的智能合约:
use anchor_lang::prelude::*;
use anchor_lang::solana_program::system_program;
declare_id!("BNDCEb5uXCuWDxJW9BGmbfvR1JBMAKckfhYrEKW2Bv1W");
#[program]
pub mod solana_twitter {
use super::*;
pub fn send_tweet(ctx: Context<SendTweet>, topic: String, content: String) -> ProgramResult {
let tweet: &mut Account<Tweet> = &mut ctx.accounts.tweet;
let author: &Signer = &ctx.accounts.author;
let clock: Clock = Clock::get().unwrap();
if topic.chars().count() > 50 {
return Err(ErrorCode::TopicTooLong.into())
}
if content.chars().count() > 280 {
return Err(ErrorCode::ContentTooLong.into())
}
tweet.author = *author.key;
tweet.timestamp = clock.unix_timestamp;
tweet.topic = topic;
tweet.content = content;
Ok(())
}
}
#[derive(Accounts)]
pub struct SendTweet<'info> {
#[account(init, payer = author, space = Tweet::LEN)]
pub tweet: Account<'info, Tweet>,
#[account(mut)]
pub author: Signer<'info>,
#[account(address = system_program::ID)]
pub system_program: AccountInfo<'info>,
}
#[account]
pub struct Tweet {
pub author: Pubkey,
pub timestamp: i64,
pub topic: String,
pub content: String,
}
#[error]
pub enum ErrorCode {
#[msg("The provided topic should be 50 characters long maximum.")]
TopicTooLong,
#[msg("The provided content should be 280 characters long maximum.")]
ContentTooLong,
}
Run Code Online (Sandbox Code Playgroud)
下面是我在 Typescript 中的测试:
import * as anchor from '@project-serum/anchor';
import { Program } from '@project-serum/anchor';
import { SolanaTwitter } from '../target/types/solana_twitter';
import * as assert from "assert";
import * as bs58 from "bs58";
describe('solana-twitter', () => {
// Configure the client to use the local cluster.
anchor.setProvider(anchor.Provider.env());
const program = anchor.workspace.SolanaTwitter as Program<SolanaTwitter>;
it('can send a new tweet', async () => {
// Call the "SendTweet" instruction.
const tweet = anchor.web3.Keypair.generate();
await program.rpc.sendTweet('veganism', 'Hummus, am I right?', {
accounts: {
tweet: tweet.publicKey,
author: program.provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
},
signers: [tweet],
});
// Fetch the account details of the created tweet.
const tweetAccount = await program.account.tweet.fetch(tweet.publicKey);
// Ensure it has the right data.
assert.equal(tweetAccount.author.toBase58(), program.provider.wallet.publicKey.toBase58());
assert.equal(tweetAccount.topic, 'veganism');
assert.equal(tweetAccount.content, 'Hummus, am I right?');
assert.ok(tweetAccount.timestamp);
});
it('can send a new tweet without a topic', async () => {
// Call the "SendTweet" instruction.
const tweet = anchor.web3.Keypair.generate();
await program.rpc.sendTweet('', 'gm', {
accounts: {
tweet: tweet.publicKey,
author: program.provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
},
signers: [tweet],
});
// Fetch the account details of the created tweet.
const tweetAccount = await program.account.tweet.fetch(tweet.publicKey);
// Ensure it has the right data.
assert.equal(tweetAccount.author.toBase58(), program.provider.wallet.publicKey.toBase58());
assert.equal(tweetAccount.topic, '');
assert.equal(tweetAccount.content, 'gm');
assert.ok(tweetAccount.timestamp);
});
it('can send a new tweet from a different author', async () => {
// Generate another user and airdrop them some SOL.
const otherUser = anchor.web3.Keypair.generate();
const signature = await program.provider.connection.requestAirdrop(otherUser.publicKey, 1000000000);
await program.provider.connection.confirmTransaction(signature);
// Call the "SendTweet" instruction on behalf of this other user.
const tweet = anchor.web3.Keypair.generate();
await program.rpc.sendTweet('veganism', 'Yay Tofu!', {
accounts: {
tweet: tweet.publicKey,
author: otherUser.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
},
signers: [otherUser, tweet],
});
// Fetch the account details of the created tweet.
const tweetAccount = await program.account.tweet.fetch(tweet.publicKey);
// Ensure it has the right data.
assert.equal(tweetAccount.author.toBase58(), otherUser.publicKey.toBase58());
assert.equal(tweetAccount.topic, 'veganism');
assert.equal(tweetAccount.content, 'Yay Tofu!');
assert.ok(tweetAccount.timestamp);
});
it('cannot provide a topic with more than 50 characters', async () => {
try {
const tweet = anchor.web3.Keypair.generate();
const topicWith51Chars = 'x'.repeat(51);
await program.rpc.sendTweet(topicWith51Chars, 'Hummus, am I right?', {
accounts: {
tweet: tweet.publicKey,
author: program.provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
},
signers: [tweet],
});
} catch (error) {
assert.equal(error.msg, 'The provided topic should be 50 characters long maximum.');
return;
}
assert.fail('The instruction should have failed with a 51-character topic.');
});
it('cannot provide a content with more than 280 characters', async () => {
try {
const tweet = anchor.web3.Keypair.generate();
const contentWith281Chars = 'x'.repeat(281);
await program.rpc.sendTweet('veganism', contentWith281Chars, {
accounts: {
tweet: tweet.publicKey,
author: program.provider.wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
},
signers: [tweet],
});
} catch (error) {
assert.equal(error.msg, 'The provided content should be 280 characters long maximum.');
return;
}
assert.fail('The instruction should have failed with a 281-character content.');
});
it('can fetch all tweets', async () => {
const tweetAccounts = await program.account.tweet.all();
assert.equal(tweetAccounts.length, 3);
});
it('can filter tweets by author', async () => {
const authorPublicKey = program.provider.wallet.publicKey
const tweetAccounts = await program.account.tweet.all([
{
memcmp: {
offset: 8, // Discriminator.
bytes: authorPublicKey.toBase58(),
}
}
]);
assert.equal(tweetAccounts.length, 2);
assert.ok(tweetAccounts.every(tweetAccount => {
return tweetAccount.account.author.toBase58() === authorPublicKey.toBase58()
}))
});
it('can filter tweets by topics', async () => {
const tweetAccounts = await program.account.tweet.all([
{
memcmp: {
offset: 8 + // Discriminator.
32 + // Author public key.
8 + // Timestamp.
4, // Topic string prefix.
bytes: bs58.encode(Buffer.from('veganism')),
}
}
]);
assert.equal(tweetAccounts.length, 2);
assert.ok(tweetAccounts.every(tweetAccount => {
return tweetAccount.account.topic === 'veganism'
}))
});
});
Run Code Online (Sandbox Code Playgroud)
更新
只需空投到特定帐户就有帮助,solana airdrop 3 <Account Address>
。另一个相关问题是我的本地集群挂起,为了解决这个问题,我按照以下步骤操作:
首先,尝试确保您的anchor.toml的provider部分设置为testnet,如下所示
[provider]
cluster = "testnet"
wallet = "/home/yourname/.config/solana/id.json"
Run Code Online (Sandbox Code Playgroud)
另外,使用此命令为测试网设置集群
solana config set --url https://api.testnet.solana.com
Run Code Online (Sandbox Code Playgroud)
检查 solana 运行节点中的活动帐户
solana address
Run Code Online (Sandbox Code Playgroud)
它应该显示您正在运行的节点活动帐户公钥
另外,检查帐户余额
solana balance
Run Code Online (Sandbox Code Playgroud)
如果您没有足够的 sol 来运行测试或部署(测试后),您可以通过运行来空投 sol
solana airdrop 2
Run Code Online (Sandbox Code Playgroud)
重点是尝试一次空投 2 个 sol(多次)
现在您可以再次运行锚点测试(锚点构建后)