技术分析:Solana如何实现账户交易?
2021-06-25 15:59:38
3410
solana官方的solana-program-library中给出了很多实用的实例,本次我们将介绍其中一个调用交易实例的例子,链接的examples中可以找到对应的rust program例程,我们用自定义的ts写的client去调用即可。
生成钱包地址并领取空投:
在发起交易前需要创建两个账户,并在其中添加相应的代币:
- 创建两个新钱包,并输出json文件
$ solana-keygen new --outfile xxx.json
- 空投x个SOL到钱包的公钥地址中
$ solana airdrop x pubkey
- cd到transfer-lamports的目录下
$ cd .../transfer-lamports
- 编译rust项目,获得json和so文件
$ cargo build-bpf
ts程序中实现调用和交易的关键步骤:
1.建立一个JSON RPC连接
- 首先获得solana的配置信息并做解析,这一步的信息也可用CLI的solana config get得出查看
async function getConfig(): Promise<any> {
// Path to Solana CLI config file
const CONFIG_FILE_PATH = path.resolve(
os.homedir(),
'.config',
'solana',
'cli',
'config.yml',
);
const configYml = await fs.readFile(CONFIG_FILE_PATH, {encoding: 'utf8'});
return yaml.parse(configYml);
}
- 接着取其中的rpc_url建立连接
const config = await getConfig();
rpcUrl = config.json_rpc_url
connection = new Connection(rpcUrl, 'confirmed');
2.设置payer交易费用支付者
- 首先计算建立账号所需的费用
const {feeCalculator} = await connection.getRecentBlockhash();
// Calculate the cost to fund the greeter account
fees += await connection.getMinimumBalanceForRentExemption(SIZE);
// Calculate the cost of sending transactions
fees += feeCalculator.lamportsPerSignature * 100; // wag
- 接着从配置文件中读取密钥对作为支付者
const config = await getConfig();
payerAccount = readAccountFromFile(config.keypair_path);
- 如果账户中的lamports余额低于最小要求时,则需要请求空投,一般这会发生在命令行配置的密钥对中
const lamports = await connection.getBalance(payerAccount.publicKey);
if (lamports < fees) {
const sig = await connection.requestAirdrop(
payerAccount.publicKey,
fees - lamports,
);
await connection.confirmTransaction(sig);
}
3.检查BPF项目是否部署
在第一步我们编译rust生成bpf文件后,并没有进行部署,这一步就是为了检查项目是否部署在链上,通过检查programId是否有关联账号来判断。
- 首先读取项目密钥对的路径文件,并获得公钥即programId,这个文件在编译后生成,一般在target/deploy目录下的json文件中
const PROGRAM_KEYPAIR_PATH = ('.../src/transfer-lamports/target/deploy/spl_example_transfer_lamports-keypair.json');
const programAccount = await readAccountFromFile(PROGRAM_KEYPAIR_PATH);
programId = programAccount.publicKey;
- 接着读取programId的账户信息,通过查看账户信息判断是否部署,如果账户是空的并且存在so文件路径,则需要进行program部署,如果不存在so文件,则需要先进行编译生成文件再进行部署。如果账户存在,但是programInfo.executable是false,则代表program已经部署但无法执行,需要进行检查修改
const programInfo = await connection.getAccountInfo(programId);
if (programInfo === null) {
if (fs.existsSync(PROGRAM_SO_PATH)) {
throw new Error(
'Program needs to be deployed with `solana program deploy dist/program/helloworld.so`',
);
} else {
throw new Error('Program needs to be built and deployed');
}
}
else if (!programInfo.executable) {
throw new Error(`Program is not executable`);
}
4.交易指令发起
在完成前面的步骤后,则可进行ts的交易指令发起编写
- 首先设置两个新钱包的json文件路径,并获取付款账号的公钥,这里为TransferOne
const TRANSFER_ONE = ('.../transfer_one.json');
const TRANSFER_TWO = ('.../transfer_two.json');
TransferOne = await readAccountFromFile(TRANSFER_ONE);
- 新建一笔交易,添加指令,指令为系统指令,修改TransferOne账号拥有者为新的programId,原拥有者是系统。
const transaction = new Transaction().add(
SystemProgram.assign({accountPubkey: TransferOne.publicKey, programId}) ,);
- 发送交易并确认,发送账号要包含费用支付账号,转账者账号。
await sendAndConfirmTransaction(
connection,
transaction,
[payerAccount,TransferOne],
);
- 新建一个交易指令,把转账者和接受者的AccountMeta,programId,空的data添加进去。
const instruction = new TransactionInstruction({
keys: [{pubkey: TransferOne.publicKey, isSigner: false, isWritable: true},
{pubkey: TransferTwo.publicKey, isSigner: false, isWritable: true}],
programId,
data: Buffer.alloc(0),
});
- 发送交易并确认
await sendAndConfirmTransaction(
connection,
new Transaction().add(instruction),
[payerAccount],
);
以上便是ts中关键的一些实现步骤,不包括全部代码,接下来便是CLi的命令执行和日志查看
程序部署执行操作:
- 设置环境为本地
solana config set --url localhost
- 打开一个新终端,启动测试验证节点
solana-test-validator
- 打开一个新终端,启动solana日志监听
solana logs
- 打开一个新终端,在已经编译生成bpf文件的基础上,部署程序到链上
solana program deploy .../src/transfer-lamports/target/deploy/spl_example_transfer_lamports.so
- 安装npm环境
npm install
- 启动程序
npm run start
- 在rust中添加交易前后打印账号信息的代码,留意lamports的余额变化
Program log: source_info AccountInfo { key: 9yYpfeaZj5BR7t7NNwtVGriTVdQRrh3onNjDRJ3Q3hxj owner: 7t8m9KKZb3bDE4ez3CKR7cMF7NVHXbo7cU6zFVGWpjKb is_signer: false is_writable: true executable: false rent_epoch: 0 lamports: 9999999970 data.len: 0 }
Program log: destination_info AccountInfo { key: 8LNqub4QPXyNZKaHJCzgYaRDDwaYBp91abDhGoFzjP4X owner: 11111111111111111111111111111111 is_signer: false is_writable: true executable: false rent_epoch: 0 lamports: 1000000030 data.len: 0 }
Program log: source_info AccountInfo { key: 9yYpfeaZj5BR7t7NNwtVGriTVdQRrh3onNjDRJ3Q3hxj owner: 7t8m9KKZb3bDE4ez3CKR7cMF7NVHXbo7cU6zFVGWpjKb is_signer: false is_writable: true executable: false rent_epoch: 0 lamports: 9999999965 data.len: 0 }
Program log: destination_info AccountInfo { key: 8LNqub4QPXyNZKaHJCzgYaRDDwaYBp91abDhGoFzjP4X owner: 11111111111111111111111111111111 is_signer: false is_writable: true executable: false rent_epoch: 0 lamports: 1000000035 data.len: 0 }
可以看到lamports的数量进行了转移,
转账成功!
声明:所有在本站发表的文章,本站都具有最终编辑权。本站全部作品均系微算力原创或来自网络转载,转载目的在于传递更多信息,并不代表本站赞同其观点和对其真实性负责,所产生的纠纷与本站无关。如涉及作品内容、版权和其它问题,请尽快与本站联系。