// pages/api/solana/balance.tsimporttype{NextApiRequest,NextApiResponse}from'next';import{Connection,PublicKey}from'@solana/web3.js';import{getNodeURL}from'@figment-solana/lib';exportdefaultasyncfunctionbalance(req:NextApiRequest,res:NextApiResponse<string|number>,){try{const{network,address}=req.body;consturl=getNodeURL(network);constconnection=newConnection(url,'confirmed');/***** START *****/constpublicKey=newPublicKey(address);// The balance is denominated in LAMPORTSconstbalance=awaitconnection.getBalance(publicKey);/***** END *****/if(balance===0||balance===undefined){thrownewError('Account not funded');}res.status(200).json(balance);}catch(error){leterrorMessage=errorinstanceofError?error.message:'Unknown Error';res.status(500).json(errorMessage);}}
// pages/api/solana/transfer.tsimporttype{NextApiRequest,NextApiResponse}from'next';import{getNodeURL}from'@figment-solana/lib';import{Connection,PublicKey,SystemProgram,Transaction,sendAndConfirmTransaction,Keypair}from'@solana/web3.js';exportdefaultasyncfunctiontransfer(req:NextApiRequest,res:NextApiResponse<string>,){try{const{address,secret,recipient,lamports,network}=req.body;consturl=getNodeURL(network);constconnection=newConnection(url,'confirmed');constfromPubkey=newPublicKey(address);consttoPubkey=newPublicKey(recipient);// The secret key is stored in our state as a stringified arrayconstsecretKey=Uint8Array.from(JSON.parse(secretasstring));/***** START *****///... let's skip the beginning as it should be familiar for you by now!// Find the parameter to passconstinstructions=SystemProgram.transfer({fromPubkey,toPubkey,lamports});// How could you construct a signer array'sconstsigners=[Keypair.fromSecretKey(secretKey)];//const signers = [{publicKey: fromPubkey, secretKey}];// 当属性名与变量名相同时,e.g. name:name, 可简写为 name// Maybe adding something to a Transaction could be interesting ?consttransaction=newTransaction().add(instructions);// We can send and confirm a transaction in one row.consthash=awaitsendAndConfirmTransaction(connection,transaction,signers);/***** END *****/res.status(200).json(hash);}catch(error){leterrorMessage=errorinstanceofError?error.message:'Unknown Error';res.status(500).json(errorMessage);}}
// The Rust source code for the program// contracts/solana/program/src/lib.rsuseborsh::{BorshDeserialize,BorshSerialize};// borsh: Binary Object Representation Serializer for Hashingusesolana_program::{account_info::{next_account_info,AccountInfo},entrypoint,entrypoint::ProgramResult,msg,// for low-impact logging on the blockchainprogram_error::ProgramError,pubkey::Pubkey,};
/// Define the type of state stored in accounts#[derive(BorshSerialize, BorshDeserialize, Debug)]pubstructGreetingAccount{/// number of greetingspubcounter:u32,}
// Declare and export the program's entrypointentrypoint!(process_instruction);// Program entrypoint's implementationpubfnprocess_instruction(program_id:&Pubkey,// Public key of the account the hello world program was loaded intoaccounts:&[AccountInfo],// The account to say hello to_instruction_data:&[u8],// Ignored, all helloworld instructions are hellos)->ProgramResult{// print messages to the Program Log with the msg!() macro// rather than use println!() which would be prohibitive in terms of computational cost for the network.msg!("Hello World Rust program entrypoint");// Iterating accounts is safer than indexingletaccounts_iter=&mutaccounts.iter();// accounts_iter takes a mutable reference of each values in accounts// Get the account to say hello toletaccount=next_account_info(accounts_iter)?;// Will return the next AccountInfo or a NotEnoughAccountKeys error// ? is a shortcut expression for error propagation// The account must be owned by the program in order to modify its data// 除所有者外,其他人无权修改数据账户的状态ifaccount.owner!=program_id{msg!("Greeted account does not have the correct program id");returnErr(ProgramError::IncorrectProgramId);}// Increment and store the number of times the account has been greetedletmutgreeting_account=GreetingAccount::try_from_slice(&account.data.borrow())?;// borrow operator &// shared borrow &: the place may not be mutated, but it may be read or shared again// mutable borrow &mut: the place may not be accessed in any way until the borrow expires// try_from_slice will mutably reference and deserialize the account.datagreeting_account.counter+=1;greeting_account.serialize(&mut&mutaccount.data.borrow_mut()[..])?;// with the serialize(), the new counter value is sent back to Solanamsg!("Greeted {} time(s)!",greeting_account.counter);Ok(())}
# Build the program, running the following command from the project root directory$yarnrunsolana:build:program
# Deploy the program$solanadeploy-v--keypairsolana-wallet/keypair.jsondist/solana/program/helloworld.so
# Default Signer Path: solana-wallet/keypair.json
// pages/api/solana/deploy.tsimporttype{NextApiRequest,NextApiResponse}from'next';import{Connection,PublicKey}from'@solana/web3.js';import{getNodeURL}from'@figment-solana/lib';importpathfrom'path';importfsfrom'mz/fs';constPROGRAM_PATH=path.resolve('dist/solana/program');constPROGRAM_SO_PATH=path.join(PROGRAM_PATH,'helloworld.so');exportdefaultasyncfunctiondeploy(req:NextApiRequest,res:NextApiResponse<string|boolean>,){try{const{network,programId}=req.body;consturl=getNodeURL(network);constconnection=newConnection(url,'confirmed');/***** START *****/constpublicKey=newPublicKey(programId);constprogramInfo=awaitconnection.getAccountInfo(publicKey);/***** END *****/if(programInfo===null){if(fs.existsSync(PROGRAM_SO_PATH)){thrownewError('Program needs to be deployed with `solana program deploy`',);}else{thrownewError('Program needs to be built and deployed');}}elseif(!programInfo.executable){thrownewError(`Program is not executable`);}res.status(200).json(true);}catch(error){leterrorMessage=errorinstanceofError?error.message:'Unknown Error';res.status(500).json(errorMessage);}}
// pages/api/solana/greeter.tsimport{Connection,PublicKey,Keypair,SystemProgram,Transaction,sendAndConfirmTransaction,}from'@solana/web3.js';importtype{NextApiRequest,NextApiResponse}from'next';import{getNodeURL}from'@figment-solana/lib';import*asborshfrom'borsh';// The state of a greeting account managed by the hello world programclassGreetingAccount{counter=0;constructor(fields:{counter:number}|undefined=undefined){if(fields){this.counter=fields.counter;}}}// Borsh schema definition for greeting accountsconstGreetingSchema=newMap([[GreetingAccount,{kind:'struct',fields:[['counter','u32']]}],]);// The expected size of each greeting account.constGREETING_SIZE=borsh.serialize(GreetingSchema,newGreetingAccount(),).length;typeResponseT={hash:string;greeter:string;};exportdefaultasyncfunctiongreeter(req:NextApiRequest,res:NextApiResponse<string|ResponseT>,){try{const{network,secret,programId:programAddress}=req.body;consturl=getNodeURL(network);constconnection=newConnection(url,'confirmed');constprogramId=newPublicKey(programAddress);constpayer=Keypair.fromSecretKey(newUint8Array(JSON.parse(secret)));constGREETING_SEED='hello';/***** START *****/// Are there any methods from PublicKey to derive a public key from a seed?constgreetedPubkey=awaitPublicKey.createWithSeed(payer.publicKey,GREETING_SEED,programId);// This function calculates the fees we have to pay to keep the newly// created account alive on the blockchain. We're naming it lamports because// that is the denomination of the amount being returned by the function.constlamports=awaitconnection.getMinimumBalanceForRentExemption(GREETING_SIZE,);// Find which instructions are expected and complete SystemProgram with// the required arguments.consttransaction=newTransaction().add(SystemProgram.createAccountWithSeed({basePubkey:payer.publicKey,// Base public key to use to derive the address of the created accountfromPubkey:payer.publicKey,// The payerlamports,newAccountPubkey:greetedPubkey,// The created accountprogramId,seed:GREETING_SEED,space:GREETING_SIZE}));// Complete this function call with the expected arguments.consthash=awaitsendAndConfirmTransaction(connection,transaction,[payer]);/***** END *****/res.status(200).json({hash:hash,greeter:greetedPubkey.toBase58(),});}catch(error){leterrorMessage=errorinstanceofError?error.message:'Unknown Error';res.status(500).json(errorMessage);}}
First deserialize the greeter data to a TypeScript class, then access the counter value and pass it to the response object using the .json() method as in all previous tutorials
// pages/api/solana/getter.tsimporttype{NextApiRequest,NextApiResponse}from'next';import{Connection,PublicKey}from'@solana/web3.js';import{getNodeURL}from'@figment-solana/lib';import*asborshfrom'borsh';// The state of a greeting account managed by the hello world programclassGreetingAccount{counter=0;constructor(fields:{counter:number}|undefined=undefined){if(fields){this.counter=fields.counter;}}}// Borsh schema definition for greeting accountsconstGreetingSchema=newMap([[GreetingAccount,{kind:'struct',fields:[['counter','u32']]}],]);exportdefaultasyncfunctiongetter(req:NextApiRequest,res:NextApiResponse<string|number>,){try{const{network,greeter}=req.body;consturl=getNodeURL(network);constconnection=newConnection(url,'confirmed');constgreeterPublicKey=newPublicKey(greeter);constaccountInfo=awaitconnection.getAccountInfo(greeterPublicKey);if(accountInfo===null){thrownewError('Error: cannot find the greeted account');}/***** START *****/// Find the expected parameters.constgreeting=borsh.deserialize(GreetingSchema,GreetingAccount,accountInfo.data);// A little helperconsole.log(greeting);// Pass the counter to the client-side as JSONres.status(200).json(greeting.counter);/***** END *****/}catch(error){leterrorMessage=errorinstanceofError?error.message:'Unknown Error';console.log(errorMessage);res.status(500).json(errorMessage);}}