From a06e73c767b6052ba30a060ccbe1162d46411a76 Mon Sep 17 00:00:00 2001 From: garikbesson Date: Wed, 9 Oct 2024 13:08:25 +0200 Subject: [PATCH 1/6] js + rust overview --- docs/4.tools/near-api.md | 1045 ++++++++++++++++++++++++++++++++++++++ website/sidebars.js | 1 + 2 files changed, 1046 insertions(+) create mode 100644 docs/4.tools/near-api.md diff --git a/docs/4.tools/near-api.md b/docs/4.tools/near-api.md new file mode 100644 index 00000000000..df15d344b03 --- /dev/null +++ b/docs/4.tools/near-api.md @@ -0,0 +1,1045 @@ +--- +id: near-api +title: NEAR API +sidebar_label: NEAR API +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## What is NEAR API + +There are differents libraries to interact with the NEAR blockchain. You can use it in the browser ([`near-api-js`](https://github.com/near/near-api-js)), or in server runtime ([`near-api-js`](https://github.com/near/near-api-js) for Node.js, [`near-jsonrpc-client`](https://github.com/near/near-jsonrpc-client-rs) for Rust and [`py-near`](https://github.com/pvolnov/py-near) for Python). + +:::tip +To quickly get started with integrating NEAR in a web browser, read our [Web Frontend integration](/build/web3-apps/integrate-contracts) article. +::: + +--- + +## Basics + +### Install {#install} + + + + Include `near-api-js` as a dependency in your package. + + ```bash + npm i --save near-api-js + ``` + + + + ```bash + cargo add near-jsonrpc-client + ``` + + + +
+ +### Import {#import} + + + + You can use the API library in the browser, or in Node.js runtime. Some features are available only in one of the environments. For example, the `WalletConnection` is only for the browser, and there are different `KeyStore` providers for each environment. + + ```js + import * as nearAPI from "near-api-js"; + ``` + + + + Each one of the valid JSON RPC methods are defined in the methods module. + ```rust + use near_jsonrpc_client::methods; + ``` + + + +
+ +### Connecting to NEAR {#connect} + + + + + The object returned from `connect` is your entry-point for all commands in the API. + To sign a transaction you'll need a [`KeyStore`](#key-store) to create a connection. + + ```js + const { connect } = nearAPI; + + const connectionConfig = { + networkId: "testnet", + keyStore: myKeyStore, // first create a key store + nodeUrl: "https://rpc.testnet.near.org", + walletUrl: "https://testnet.mynearwallet.com/", + helperUrl: "https://helper.testnet.near.org", + explorerUrl: "https://testnet.nearblocks.io", + }; + const nearConnection = await connect(connectionConfig); + ``` + +
+ Mainnet/Localnet connection + + ```js + // Mainnet config example + const connectionConfig = { + networkId: "mainnet", + keyStore: myKeyStore, // first create a key store + nodeUrl: "https://rpc.mainnet.near.org", + walletUrl: "https://wallet.mainnet.near.org", + helperUrl: "https://helper.mainnet.near.org", + explorerUrl: "https://nearblocks.io", + }; + + // Localnet config example + const connectionConfig = { + networkId: "local", + nodeUrl: "http://localhost:3030", + walletUrl: "http://localhost:4000/wallet", + }; + ``` +
+ +
+ + #### Key Store + + If you sign transactions, you need to create a _Key Store_. In the browser, the LocalStorage KeyStore will be used once you ask your user to Sign In with the Wallet. + + ```js + /* Browser + Creates keyStore using private key in local storage + */ + const { keyStores } = nearAPI; + const myKeyStore = new keyStores.BrowserLocalStorageKeyStore(); + + /* Credentials Directory + Creates a keyStore that searches for keys in .near-credentials + Requires credentials stored locally by using a NEAR-CLI command: `near login` + https://docs.near.org/tools/cli#near-login + */ + const { keyStores } = nearAPI; + const homedir = require("os").homedir(); + const CREDENTIALS_DIR = ".near-credentials"; + const credentialsPath = require("path").join(homedir, CREDENTIALS_DIR); + const myKeyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); + + /* File + Creates keyStore from a provided file + You will need to pass the location of the .json key pair + */ + const { KeyPair, keyStores } = require("near-api-js"); + const fs = require("fs"); + const homedir = require("os").homedir(); + + const ACCOUNT_ID = "near-example.testnet"; // NEAR account tied to the keyPair + const NETWORK_ID = "testnet"; + // path to your custom keyPair location (ex. function access key for example account) + const KEY_PATH = "/.near-credentials/near-example-testnet/get_token_price.json"; + + const credentials = JSON.parse(fs.readFileSync(homedir + KEY_PATH)); + const myKeyStore = new keyStores.InMemoryKeyStore(); + myKeyStore.setKey( + NETWORK_ID, + ACCOUNT_ID, + KeyPair.fromString(credentials.private_key) + ); + + /* Private key string + Creates keyStore from a private key string + You can define your key here or use an environment variable + */ + const { keyStores, KeyPair } = nearAPI; + const myKeyStore = new keyStores.InMemoryKeyStore(); + const PRIVATE_KEY = + "by8kdJoJHu7uUkKfoaLd2J2Dp1q1TigeWMG123pHdu9UREqPcshCM223kWadm"; + // creates a public / private key pair using the provided private key + const keyPair = KeyPair.fromString(PRIVATE_KEY); + // adds the keyPair you created to keyStore + await myKeyStore.setKey("testnet", "example-account.testnet", keyPair); + ``` +
+ + + ```rust + use near_jsonrpc_client::JsonRpcClient; + + let testnet_client = JsonRpcClient::connect("https://rpc.testnet.near.org"); + ``` + +
+ +
+ +### RPC Failover + +RPC providers can experience intermittent downtime, connectivity issues, or rate limits that cause client transactions to fail. This can be prevented by using the `FailoverRpcProvider` that supports multiple RPC providers. + + + + + ```js + const jsonProviders = [ + new JsonRpcProvider({ + url: 'https://rpc.mainnet.near.org', + }), + new JsonRpcProvider( + { + url: 'https://another-rpc.cloud.com', + headers: { 'X-Api-Key': 'some string' }, + }, + { retries: 3, backoff: 2, wait: 500 } + ), + ]; + const provider = new FailoverRpcProvider(jsonProviders); + + await connect({ + networkId: 'mainnet', + provider: provider, + // this isn't used if `provider` is specified, but is still required for backward compatibility + nodeUrl: 'https://rpc.mainnet.near.org', + }); + ``` + + + + +--- + +## Account + +### Get Account {#get-account} + +This will return an Account object for you to interact with. + + + + + ```js + const account = await nearConnection.account("example-account.testnet"); + ``` + + + + +
+ +### Get Account Balance {#get-account-balance} + + + + + ```js + // gets account balance + const account = await nearConnection.account("example-account.testnet"); + const accountBalance = await account.getAccountBalance(); + ``` + + + + +
+ +### Get Account Details {#get-account-details} + +Returns information about an account, such as authorized apps. + + + + + + ```js + // gets account details in terms of authorized apps and transactions + const account = await nearConnection.account("example-account.testnet"); + const accountDetails = await account.getAccountDetails(); + ``` + + + + +
+ +### Get Account State {#get-account-state} + +Get basic account information, such as amount of tokens the account has or the amount of storage it uses. + + + + + ```js + const account = await nearConnection.account("example-account.testnet"); + const accountState = await account.state(); + ``` + + + + ```rust + use near_jsonrpc_client::methods; + use near_jsonrpc_primitives::types::query::QueryResponseKind; + use near_primitives::types::{AccountId, BlockReference, Finality}; + use near_primitives::views::QueryRequest; + + mod utils; + + #[tokio::main] + async fn main() -> Result<(), Box> { + env_logger::init(); + + let client = utils::select_network()?; + + let account_id: AccountId = utils::input("Enter an Account ID to lookup: ")?.parse()?; + + let request = methods::query::RpcQueryRequest { + block_reference: BlockReference::Finality(Finality::Final), + request: QueryRequest::ViewAccount { account_id }, + }; + + let response = client.call(request).await?; + + if let QueryResponseKind::ViewAccount(result) = response.kind { + println!("{:#?}", result); + } + + Ok(()) + } + ``` + + + +
+ +### Create Account {#create-account} + +Create a sub-account. + + + + + ```js + // creates a sub-account using funds from the account used to create it. + const account = await nearConnection.account("example-account.testnet"); + await account.createAccount( + "sub.example-account.testnet", // sub-account name + "8hSHprDq2StXwMtNd43wDTXQYsjXcD4MJTXQYsjXcc", // public key for sub account + "10000000000000000000" // initial balance for new account in yoctoNEAR + ); + ``` + + For creating .near or .testnet accounts please refer to the [cookbook](https://github.com/near/near-api-js/tree/master/packages/cookbook/accounts). + + + + + ```rust + //! Creates an account on the network. + //! + //! Creates either; + //! - a top-level mainnet / testnet account + //! - or a sub-account for any account on the network. + //! + //! top-level account example: `miraclx.near` creates `foobar.near` + //! sub-account example: `miraclx.near` creates `test.miraclx.near` + //! + //! This script is interactive. + + use near_crypto::Signer; + use near_jsonrpc_client::methods::broadcast_tx_commit::RpcTransactionError; + use near_jsonrpc_client::{methods, JsonRpcClient}; + use near_jsonrpc_primitives::types::query::QueryResponseKind; + use near_jsonrpc_primitives::types::transactions::TransactionInfo; + use near_primitives::hash::CryptoHash; + use near_primitives::transaction::{ + Action, AddKeyAction, CreateAccountAction, FunctionCallAction, Transaction, TransactionV0, + TransferAction, + }; + use near_primitives::types::{AccountId, BlockReference}; + use near_primitives::views::{FinalExecutionStatus, TxExecutionStatus}; + + use serde_json::json; + use tokio::time; + + mod utils; + + async fn account_exists( + client: &JsonRpcClient, + account_id: &AccountId, + ) -> Result> { + let access_key_query_response = client + .call(methods::query::RpcQueryRequest { + block_reference: BlockReference::latest(), + request: near_primitives::views::QueryRequest::ViewAccount { + account_id: account_id.clone(), + }, + }) + .await; + + match access_key_query_response { + Ok(_) => Ok(true), + Err(near_jsonrpc_client::errors::JsonRpcError::ServerError( + near_jsonrpc_client::errors::JsonRpcServerError::HandlerError( + near_jsonrpc_primitives::types::query::RpcQueryError::UnknownAccount { .. }, + ), + )) => Ok(false), + Err(res) => Err(res)?, + } + } + + async fn get_current_nonce( + client: &JsonRpcClient, + account_id: &AccountId, + public_key: &near_crypto::PublicKey, + ) -> Result, Box> { + let query_response = client + .call(methods::query::RpcQueryRequest { + block_reference: BlockReference::latest(), + request: near_primitives::views::QueryRequest::ViewAccessKey { + account_id: account_id.clone(), + public_key: public_key.clone(), + }, + }) + .await; + + match query_response { + Ok(access_key_query_response) => match access_key_query_response.kind { + QueryResponseKind::AccessKey(access_key) => Ok(Some(( + access_key_query_response.block_hash, + access_key.nonce, + ))), + _ => Err("failed to extract current nonce")?, + }, + Err(near_jsonrpc_client::errors::JsonRpcError::ServerError( + near_jsonrpc_client::errors::JsonRpcServerError::HandlerError( + near_jsonrpc_primitives::types::query::RpcQueryError::UnknownAccessKey { .. }, + ), + )) => Ok(None), + Err(res) => Err(res)?, + } + } + + #[tokio::main] + async fn main() -> Result<(), Box> { + env_logger::init(); + + let client = utils::select_network()?; + + let signer_account_id = loop { + let signer_account_id = utils::input("Enter the creators Account ID: ")?.parse()?; + if account_exists(&client, &signer_account_id).await? { + break signer_account_id; + } + println!("(i) This account doesn't exist, please reenter!"); + }; + + let (signer, latest_hash, current_nonce) = loop { + let signer_secret_key = utils::input("Enter the creators's private key: ")?.parse()?; + + let signer = near_crypto::InMemorySigner::from_secret_key( + signer_account_id.clone(), + signer_secret_key, + ); + + if let Some((latest_hash, current_nonce)) = + get_current_nonce(&client, &signer.account_id, &signer.public_key).await? + { + break (signer, latest_hash, current_nonce); + } + println!("(i) Invalid access key, please reenter!"); + }; + + let new_account_id = loop { + let new_account_id = utils::input("What's the new Account ID: ")?.parse()?; + if !account_exists(&client, &new_account_id).await? { + break new_account_id; + } + println!("(i) This account already exists, please reenter!"); + }; + + let initial_deposit = loop { + let deposit: f64 = + utils::input("How much do you want to fund this account with (in Ⓝ units)? ")? + .parse()?; + if deposit >= 0.0 { + break ((deposit * 1_000_000.0) as u128) * 1_000_000_000_000_000_000_u128; + } + println!("(i) Enter a non-zero deposit value!"); + }; + + let is_sub_account = new_account_id.is_sub_account_of(&signer.account_id); + let new_key_pair = near_crypto::SecretKey::from_random(near_crypto::KeyType::ED25519); + + let (transaction, expected_output) = if is_sub_account { + ( + TransactionV0 { + signer_id: signer.account_id.clone(), + public_key: signer.public_key.clone(), + nonce: current_nonce + 1, + receiver_id: new_account_id.clone(), + block_hash: latest_hash, + actions: vec![ + Action::CreateAccount(CreateAccountAction {}), + Action::AddKey(Box::new(AddKeyAction { + access_key: near_primitives::account::AccessKey { + nonce: 0, + permission: near_primitives::account::AccessKeyPermission::FullAccess, + }, + public_key: new_key_pair.public_key(), + })), + Action::Transfer(TransferAction { + deposit: initial_deposit, + }), + ], + }, + vec![], + ) + } else { + let contract_id = if client.server_addr().ends_with("testnet.near.org") { + "testnet".parse()? + } else if client.server_addr().ends_with("mainnet.near.org") { + "near".parse()? + } else { + Err("can only create non-sub accounts for mainnet / testnet\nconsider creating a sub-account instead")? + }; + ( + TransactionV0 { + signer_id: signer.account_id.clone(), + public_key: signer.public_key.clone(), + nonce: current_nonce + 1, + receiver_id: contract_id, + block_hash: latest_hash, + actions: vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "create_account".to_string(), + args: json!({ + "new_account_id": new_account_id, + "new_public_key": new_key_pair.public_key(), + }) + .to_string() + .into_bytes(), + gas: 300_000_000_000_000, + deposit: initial_deposit, + }))], + }, + b"true".to_vec(), + ) + }; + + println!("============================================================="); + println!("New Account ID: {}", new_account_id); + println!(" Secret Key: {}", new_key_pair); + println!(" Public Key: {}", new_key_pair.public_key()); + println!(" Deposit: {}", initial_deposit); + println!("-------------------------------------------------------------"); + + let request = methods::broadcast_tx_async::RpcBroadcastTxAsyncRequest { + signed_transaction: Transaction::V0(transaction).sign(&Signer::InMemory(signer.clone())), + }; + + let sent_at = time::Instant::now(); + + let tx_hash = client.call(request).await?; + + println!(" Tx Hash: {}", tx_hash); + println!("============================================================="); + + loop { + let response = client + .call(methods::tx::RpcTransactionStatusRequest { + transaction_info: TransactionInfo::TransactionId { + tx_hash, + sender_account_id: signer.account_id.clone(), + }, + wait_until: TxExecutionStatus::Final, + }) + .await; + let received_at = time::Instant::now(); + let delta = (received_at - sent_at).as_secs(); + + if delta > 60 { + Err("time limit exceeded for the transaction to be recognized")?; + } + + match response { + Ok(tx) => { + // it's fine to unwrap because we asked for finalized tx + let outcome = tx.final_execution_outcome.unwrap().into_outcome(); + match outcome.status { + FinalExecutionStatus::Failure(err) => { + println!("{:#?}", err); + println!("(!) Creating the account failed, check above for full logs"); + break; + } + FinalExecutionStatus::SuccessValue(ref s) => { + if s == &expected_output { + println!("(i) Account successfully created after {}s", delta); + } else { + println!("{:#?}", outcome); + println!("(!) Creating the account failed, check above for full logs"); + } + break; + } + _ => {} + } + } + Err(err) => match err.handler_error() { + Some( + RpcTransactionError::TimeoutError + | RpcTransactionError::UnknownTransaction { .. }, + ) => { + time::sleep(time::Duration::from_secs(2)).await; + continue; + } + _ => Err(err)?, + }, + } + } + + Ok(()) + } + ``` + + + +
+ +### Delete Account {#delete-account} + + + + + ```js + // deletes account found in the `account` object + // transfers remaining account balance to the accountId passed as an argument + const account = await nearConnection.account("example-account.testnet"); + await account.deleteAccount("beneficiary-account.testnet"); + ``` + + + + +--- + +## Transactions + +### Send Tokens {#send-tokens} + +Transfer NEAR tokens between accounts. This returns an object with transaction and receipts outcomes and status. + + + + + ```js + const account = await nearConnection.account("sender-account.testnet"); + await account.sendMoney( + "receiver-account.testnet", // receiver account + "1000000000000000000000000" // amount in yoctoNEAR + ); + ``` + + + +
+ +### View + + + + + ```js + import { providers } from 'near-api-js'; + + const url = `https://rpc.${this.networkId}.near.org`; + const provider = new providers.JsonRpcProvider({ url }); + + const response = await provider.query({ + request_type: 'call_function', + account_id: contractId, + method_name: method, + args_base64: Buffer.from(JSON.stringify(args)).toString('base64'), + finality: 'optimistic', + }); + + const result = JSON.parse(Buffer.from(res.result).toString()); + ``` + + + + ```rust + use near_jsonrpc_client::{methods, JsonRpcClient}; + use near_jsonrpc_primitives::types::query::QueryResponseKind; + use near_primitives::types::{BlockReference, Finality, FunctionArgs}; + use near_primitives::views::QueryRequest; + + use serde::Deserialize; + use serde_json::{from_slice, json}; + + mod utils; + + #[derive(Debug, Deserialize)] + pub struct AccountStatus { + pub rating: f32, + pub given: u64, + pub received: u64, + } + + #[tokio::main] + async fn main() -> Result<(), Box> { + env_logger::init(); + + let client = JsonRpcClient::connect("https://rpc.testnet.near.org"); + + let account_id = utils::input("Enter the account to view: ")?; + + let request = methods::query::RpcQueryRequest { + block_reference: BlockReference::Finality(Finality::Final), + request: QueryRequest::CallFunction { + account_id: "nosedive.testnet".parse()?, + method_name: "status".to_string(), + args: FunctionArgs::from( + json!({ + "account_id": account_id, + }) + .to_string() + .into_bytes(), + ), + }, + }; + + let response = client.call(request).await?; + + if let QueryResponseKind::CallResult(result) = response.kind { + println!("{:#?}", from_slice::(&result.result)?); + } + + Ok(()) + } + ``` + + + +
+ +### Call + + + + + ```js + import { connect, transactions, keyStores } from "near-api-js"; + + const account = await nearConnection.account("example-account.testnet"); + const result = await account.signAndSendTransaction({ + receiverId: "example-contract.testnet", + actions: [ + transactions.functionCall( + "new", + Buffer.from(JSON.stringify(newArgs)), + 10000000000000, + "0" + ), + ], + }); + ``` + + + + ```rust + use near_crypto::Signer; + use near_jsonrpc_client::{methods, JsonRpcClient}; + use near_jsonrpc_primitives::types::query::QueryResponseKind; + use near_jsonrpc_primitives::types::transactions::{RpcTransactionError, TransactionInfo}; + use near_primitives::transaction::{Action, FunctionCallAction, Transaction, TransactionV0}; + use near_primitives::types::BlockReference; + use near_primitives::views::TxExecutionStatus; + + use serde_json::json; + use tokio::time; + + mod utils; + + #[tokio::main] + async fn main() -> Result<(), Box> { + env_logger::init(); + + let client = JsonRpcClient::connect("https://rpc.testnet.near.org"); + + let signer_account_id = utils::input("Enter the signer Account ID: ")?.parse()?; + let signer_secret_key = utils::input("Enter the signer's private key: ")?.parse()?; + + let signer = near_crypto::InMemorySigner::from_secret_key(signer_account_id, signer_secret_key); + + let access_key_query_response = client + .call(methods::query::RpcQueryRequest { + block_reference: BlockReference::latest(), + request: near_primitives::views::QueryRequest::ViewAccessKey { + account_id: signer.account_id.clone(), + public_key: signer.public_key.clone(), + }, + }) + .await?; + + let current_nonce = match access_key_query_response.kind { + QueryResponseKind::AccessKey(access_key) => access_key.nonce, + _ => Err("failed to extract current nonce")?, + }; + + let other_account = utils::input("Enter the account to be rated: ")?; + let rating = utils::input("Enter a rating: ")?.parse::()?; + + let transaction = TransactionV0 { + signer_id: signer.account_id.clone(), + public_key: signer.public_key.clone(), + nonce: current_nonce + 1, + receiver_id: "nosedive.testnet".parse()?, + block_hash: access_key_query_response.block_hash, + actions: vec![Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "rate".to_string(), + args: json!({ + "account_id": other_account, + "rating": rating, + }) + .to_string() + .into_bytes(), + gas: 100_000_000_000_000, // 100 TeraGas + deposit: 0, + }))], + }; + + let request = methods::broadcast_tx_async::RpcBroadcastTxAsyncRequest { + signed_transaction: Transaction::V0(transaction).sign(&Signer::InMemory(signer.clone())), + }; + + let sent_at = time::Instant::now(); + let tx_hash = client.call(request).await?; + + loop { + let response = client + .call(methods::tx::RpcTransactionStatusRequest { + transaction_info: TransactionInfo::TransactionId { + tx_hash, + sender_account_id: signer.account_id.clone(), + }, + wait_until: TxExecutionStatus::Executed, + }) + .await; + let received_at = time::Instant::now(); + let delta = (received_at - sent_at).as_secs(); + + if delta > 60 { + Err("time limit exceeded for the transaction to be recognized")?; + } + + match response { + Err(err) => match err.handler_error() { + Some( + RpcTransactionError::TimeoutError + | RpcTransactionError::UnknownTransaction { .. }, + ) => { + time::sleep(time::Duration::from_secs(2)).await; + continue; + } + _ => Err(err)?, + }, + Ok(response) => { + println!("response gotten after: {}s", delta); + println!("response: {:#?}", response); + break; + } + } + } + + Ok(()) + } + ``` + + + +
+ +### Deploy a Contract {#deploy-a-contract} + +You can deploy a contract from a compiled WASM file. This returns an object with transaction and receipts outcomes and status. + + + + + ```js + const account = await nearConnection.account("example-account.testnet"); + const transactionOutcome = await account.deployContract( + fs.readFileSync("example-file.wasm") + ); + ``` + + + +--- + +## Keys + +You can get and manage keys for an account. + +### Add Function Access Key {#add-function-access-key} + + + + + ```js + const account = await nearConnection.account("example-account.testnet"); + await account.addKey( + "8hSHprDq2StXwMtNd43wDTXQYsjXcD4MJTXQYsjXcc", // public key for new account + "example-account.testnet", // contract this key is allowed to call (optional) + "example_method", // methods this key is allowed to call (optional) + "2500000000000" // allowance key can use to call methods (optional) + ); + ``` + + + + +
+ +### Add Full Access Key {#add-full-access-key} + + + + + ```js + // takes public key as string for argument + const account = await nearConnection.account("example-account.testnet"); + await account.addKey("8hSHprDq2StXwMtNd43wDTXQYsjXcD4MJTXQYsjXcc"); + ``` + + + + +
+ +### Get All Access Keys {#get-all-access-keys} + + + + + ```js + const account = await nearConnection.account("example-account.testnet"); + await account.getAccessKeys(); + ``` + + + + + ```rust + use near_jsonrpc_client::methods; + use near_jsonrpc_primitives::types::query::QueryResponseKind; + use near_primitives::types::BlockReference; + + mod utils; + + fn indent(indentation: usize, s: String) -> String { + let mut lines = s.split_inclusive("\n"); + let mut r = lines.next().unwrap().to_string(); + for l in lines { + r.push_str(&" ".repeat(indentation - 3)); + r.push_str("\x1b[38;5;244m>\x1b[0m "); + r.push_str(l); + } + r + } + + #[tokio::main] + async fn main() -> Result<(), Box> { + env_logger::init(); + + let client = utils::select_network()?; + + let account_id = utils::input("Enter the Account ID whose keys we're listing: ")?.parse()?; + + let access_key_query_response = client + .call(methods::query::RpcQueryRequest { + block_reference: BlockReference::latest(), + request: near_primitives::views::QueryRequest::ViewAccessKeyList { account_id }, + }) + .await?; + + if let QueryResponseKind::AccessKeyList(response) = access_key_query_response.kind { + for access_key in response.keys { + println!("🗝 [{}]", access_key.public_key); + println!(" \u{21b3} nonce: {}", access_key.access_key.nonce); + println!( + " \u{21b3} permission: {}", + indent(20, format!("{:#?}", access_key.access_key.permission)) + ); + } + } + + Ok(()) + } + ``` + + + +
+ +### Delete Access Key {#delete-access-key} + + + + + ```js + const account = await nearConnection.account("example-account.testnet"); + await account.deleteKey("8hSHprDq2StXwMtNd43wDTXQYsjXcD4MJTXQYsjXcc"); + ``` + + + + +--- + +## Utilities + +### NEAR => yoctoNEAR {#near--yoctonear} + + + + + ```js + // converts NEAR amount into yoctoNEAR (10^-24) + + const { utils } = nearAPI; + const amountInYocto = utils.format.parseNearAmount("1"); + ``` + + + + +
+ +### YoctoNEAR => NEAR {#yoctonear--near} + + + + + ```js + // converts yoctoNEAR (10^-24) amount into NEAR + + const { utils } = nearAPI; + const amountInNEAR = utils.format.formatNearAmount("1000000000000000000000000"); + ``` + + + + +--- + +## Additional resources + + + + + - [Handling Passphrases](https://github.com/near/near-seed-phrase) + - [Type Docs](https://near.github.io/near-api-js) + + + \ No newline at end of file diff --git a/website/sidebars.js b/website/sidebars.js index 54c61defd8b..54d82eac36b 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -529,6 +529,7 @@ const sidebar = { "type": "html", "value": " Developer Tools " }, + "tools/near-api", { "type": "category", "label": "JavaScript API", From 8d0ffcb9f1a38c95d68d6ae211a7d9d4e61f3bca Mon Sep 17 00:00:00 2001 From: gagdiez Date: Wed, 9 Oct 2024 15:00:26 +0200 Subject: [PATCH 2/6] minor improvements --- docs/4.tools/near-api.md | 87 ++++++++++++++++++++++++++++-------- website/docusaurus.config.js | 2 +- 2 files changed, 70 insertions(+), 19 deletions(-) diff --git a/docs/4.tools/near-api.md b/docs/4.tools/near-api.md index df15d344b03..9056f58379c 100644 --- a/docs/4.tools/near-api.md +++ b/docs/4.tools/near-api.md @@ -7,19 +7,22 @@ sidebar_label: NEAR API import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -## What is NEAR API +The NEAR API is a set of libraries that allow you to interact with the NEAR blockchain. You can use it to create accounts, send tokens, deploy contracts, and more. -There are differents libraries to interact with the NEAR blockchain. You can use it in the browser ([`near-api-js`](https://github.com/near/near-api-js)), or in server runtime ([`near-api-js`](https://github.com/near/near-api-js) for Node.js, [`near-jsonrpc-client`](https://github.com/near/near-jsonrpc-client-rs) for Rust and [`py-near`](https://github.com/pvolnov/py-near) for Python). +The API is available in multiple languages, including: +- JavaScript: [`near-api-js`](https://github.com/near/near-api-js) +- Rust: [`near-jsonrpc-client`](https://github.com/near/near-jsonrpc-client-rs) +- Python: [`py-near`](https://github.com/pvolnov/py-near) -:::tip -To quickly get started with integrating NEAR in a web browser, read our [Web Frontend integration](/build/web3-apps/integrate-contracts) article. +For example, you could use [`near-api-js`](https://github.com/near/near-api-js) to create web applications or backend services written in node.js servers. + +:::tip Wallet Integration +To allow users to login into your web application using a wallet you will need the `wallet-selector`. Read more in our [Web Frontend integration](/build/web3-apps/integrate-contracts) article ::: --- -## Basics - -### Install {#install} +## Install {#install} @@ -28,6 +31,15 @@ To quickly get started with integrating NEAR in a web browser, read our [Web Fro ```bash npm i --save near-api-js ``` + + :::tip Static HTML + If you are building a site without using `npm`, you can include the library directly in your HTML file through a CDN. + + ```html + + ``` + ::: + @@ -111,6 +123,21 @@ To quickly get started with integrating NEAR in a web browser, read our [Web Fro If you sign transactions, you need to create a _Key Store_. In the browser, the LocalStorage KeyStore will be used once you ask your user to Sign In with the Wallet. + + + + ```js + Hello + ``` + + + + ```js + World + ``` + + + ```js /* Browser Creates keyStore using private key in local storage @@ -213,7 +240,7 @@ RPC providers can experience intermittent downtime, connectivity issues, or rate ## Account -### Get Account {#get-account} +### Instantiate Account {#load-account} This will return an Account object for you to interact with. @@ -227,9 +254,13 @@ This will return an Account object for you to interact with. +:::warning +In order to be able to use the account, its credentials must be stored in the [key store](#key-store) +::: +
-### Get Account Balance {#get-account-balance} +### Get Balance {#get-account-balance} @@ -245,11 +276,10 @@ This will return an Account object for you to interact with.
-### Get Account Details {#get-account-details} +### Get Details {#get-account-details} Returns information about an account, such as authorized apps. - @@ -264,7 +294,7 @@ Returns information about an account, such as authorized apps.
-### Get Account State {#get-account-state} +### Get State {#get-account-state} Get basic account information, such as amount of tokens the account has or the amount of storage it uses. @@ -313,7 +343,7 @@ Get basic account information, such as amount of tokens the account has or the a
-### Create Account {#create-account} +### Create Sub-Account {#create-account} Create a sub-account. @@ -330,7 +360,25 @@ Create a sub-account. ); ``` - For creating .near or .testnet accounts please refer to the [cookbook](https://github.com/near/near-api-js/tree/master/packages/cookbook/accounts). +
+ Creating .near or .testnet accounts + + In order to create .near or .testnet accounts, you need to make a function call to the top-level-domain (i.e. `near` or `testnet`), calling `create_account`: + + ```js + return await creatorAccount.functionCall({ + contractId: "testnet", + methodName: "create_account", + args: { + new_account_id: "new-account.testnet", + new_public_key: "ed25519:2ASWccunZMBSygADWG2pXuHM6jWdnzLzWFU6r7wtaHYt", + }, + gas: "300000000000000", + attachedDeposit: utils.format.parseNearAmount(amount), + }); + ``` + +
@@ -643,7 +691,9 @@ Transfer NEAR tokens between accounts. This returns an object with transaction a
-### View +### View Function + +View functions are read-only functions that don't change the state of the contract. We can call these functions without instantiating an account or a key store. @@ -653,11 +703,12 @@ Transfer NEAR tokens between accounts. This returns an object with transaction a const url = `https://rpc.${this.networkId}.near.org`; const provider = new providers.JsonRpcProvider({ url }); + const args = { greeting: 'hello' }; const response = await provider.query({ request_type: 'call_function', - account_id: contractId, - method_name: method, + account_id: 'hello.near-examples.testnet', + method_name: 'get_greeting', args_base64: Buffer.from(JSON.stringify(args)).toString('base64'), finality: 'optimistic', }); @@ -722,7 +773,7 @@ Transfer NEAR tokens between accounts. This returns an object with transaction a
-### Call +### Call Function diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 62a8bb7bbfc..1385eac31ae 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -148,7 +148,7 @@ const config = { type: 'html', value: '
Essentials ', }, - { label: 'NEAR API', to: '/tools/near-api-js/quick-reference' }, + { label: 'NEAR API', to: '/tools/near-api' }, { label: 'NEAR SDK', to: '/tools/sdk' }, { label: 'NEAR CLI', to: '/tools/near-cli' }, { From b75bfddc85bee4cd40f8958e382543c10bf85d8a Mon Sep 17 00:00:00 2001 From: garikbesson Date: Wed, 9 Oct 2024 16:56:41 +0200 Subject: [PATCH 3/6] style fixes --- docs/4.tools/near-api.md | 260 +++++++++++++++++++++++++++++---------- 1 file changed, 196 insertions(+), 64 deletions(-) diff --git a/docs/4.tools/near-api.md b/docs/4.tools/near-api.md index 9056f58379c..4862b8f7cfe 100644 --- a/docs/4.tools/near-api.md +++ b/docs/4.tools/near-api.md @@ -22,10 +22,10 @@ To allow users to login into your web application using a wallet you will need t --- -## Install {#install} +## Install - + Include `near-api-js` as a dependency in your package. ```bash @@ -41,7 +41,7 @@ To allow users to login into your web application using a wallet you will need t ::: - + ```bash cargo add near-jsonrpc-client @@ -54,14 +54,14 @@ To allow users to login into your web application using a wallet you will need t ### Import {#import} - + You can use the API library in the browser, or in Node.js runtime. Some features are available only in one of the environments. For example, the `WalletConnection` is only for the browser, and there are different `KeyStore` providers for each environment. ```js import * as nearAPI from "near-api-js"; ``` - + Each one of the valid JSON RPC methods are defined in the methods module. ```rust @@ -75,7 +75,7 @@ To allow users to login into your web application using a wallet you will need t ### Connecting to NEAR {#connect} - + The object returned from `connect` is your entry-point for all commands in the API. To sign a transaction you'll need a [`KeyStore`](#key-store) to create a connection. @@ -123,43 +123,38 @@ To allow users to login into your web application using a wallet you will need t If you sign transactions, you need to create a _Key Store_. In the browser, the LocalStorage KeyStore will be used once you ask your user to Sign In with the Wallet. - - - - ```js - Hello - ``` - - - - ```js - World - ``` - - + + ```js - /* Browser - Creates keyStore using private key in local storage - */ + // creates keyStore using private key in local storage + const { keyStores } = nearAPI; const myKeyStore = new keyStores.BrowserLocalStorageKeyStore(); + ``` + + + + + ```js + // creates a keyStore that searches for keys in .near-credentials + // requires credentials stored locally by using a NEAR-CLI command: `near login` + // https://docs.near.org/tools/cli#near-login - /* Credentials Directory - Creates a keyStore that searches for keys in .near-credentials - Requires credentials stored locally by using a NEAR-CLI command: `near login` - https://docs.near.org/tools/cli#near-login - */ const { keyStores } = nearAPI; const homedir = require("os").homedir(); const CREDENTIALS_DIR = ".near-credentials"; const credentialsPath = require("path").join(homedir, CREDENTIALS_DIR); const myKeyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); + ``` + + + + + ```js + // creates keyStore from a provided file + // you will need to pass the location of the .json key pair - /* File - Creates keyStore from a provided file - You will need to pass the location of the .json key pair - */ const { KeyPair, keyStores } = require("near-api-js"); const fs = require("fs"); const homedir = require("os").homedir(); @@ -176,11 +171,15 @@ To allow users to login into your web application using a wallet you will need t ACCOUNT_ID, KeyPair.fromString(credentials.private_key) ); + ``` + + + + + ```js + // creates keyStore from a private key string + // you can define your key here or use an environment variable - /* Private key string - Creates keyStore from a private key string - You can define your key here or use an environment variable - */ const { keyStores, KeyPair } = nearAPI; const myKeyStore = new keyStores.InMemoryKeyStore(); const PRIVATE_KEY = @@ -190,8 +189,33 @@ To allow users to login into your web application using a wallet you will need t // adds the keyPair you created to keyStore await myKeyStore.setKey("testnet", "example-account.testnet", keyPair); ``` + + + + +
+ Window error using `Node.js` + + You're maybe using a KeyStore that's for the browser. Instead, use a [filesystem key](/tools/near-api-js/quick-reference#key-store) or private key string. + + **Browser KeyStore:** + + ```js + const { keyStores } = require("near-api-js"); + const keyStore = new keyStores.BrowserLocalStorageKeyStore(); + ``` + + **FileSystem KeyStore:** + + ```js + const { keyStores } = require("near-api-js"); + const KEY_PATH = "~./near-credentials/testnet/example-account.json"; + const keyStore = new keyStores.UnencryptedFileSystemKeyStore(KEY_PATH); + ``` +
+
- + ```rust use near_jsonrpc_client::JsonRpcClient; @@ -208,7 +232,7 @@ To allow users to login into your web application using a wallet you will need t RPC providers can experience intermittent downtime, connectivity issues, or rate limits that cause client transactions to fail. This can be prevented by using the `FailoverRpcProvider` that supports multiple RPC providers. - + ```js const jsonProviders = [ @@ -240,12 +264,12 @@ RPC providers can experience intermittent downtime, connectivity issues, or rate ## Account -### Instantiate Account {#load-account} +### Instantiate Account {#instantiate-account} This will return an Account object for you to interact with. - + ```js const account = await nearConnection.account("example-account.testnet"); @@ -260,10 +284,10 @@ In order to be able to use the account, its credentials must be stored in the [k
-### Get Balance {#get-account-balance} +### Get Balance {#get-balance} - + ```js // gets account balance @@ -276,12 +300,12 @@ In order to be able to use the account, its credentials must be stored in the [k
-### Get Details {#get-account-details} +### Get Details {#get-details} Returns information about an account, such as authorized apps. - + ```js // gets account details in terms of authorized apps and transactions @@ -294,19 +318,19 @@ Returns information about an account, such as authorized apps.
-### Get State {#get-account-state} +### Get State {#get-state} Get basic account information, such as amount of tokens the account has or the amount of storage it uses. - + ```js const account = await nearConnection.account("example-account.testnet"); const accountState = await account.state(); ``` - + ```rust use near_jsonrpc_client::methods; @@ -348,7 +372,7 @@ Get basic account information, such as amount of tokens the account has or the a Create a sub-account. - + ```js // creates a sub-account using funds from the account used to create it. @@ -381,7 +405,7 @@ Create a sub-account. - + ```rust //! Creates an account on the network. @@ -656,7 +680,7 @@ Create a sub-account. ### Delete Account {#delete-account} - + ```js // deletes account found in the `account` object @@ -677,7 +701,7 @@ Create a sub-account. Transfer NEAR tokens between accounts. This returns an object with transaction and receipts outcomes and status. - + ```js const account = await nearConnection.account("sender-account.testnet"); @@ -696,7 +720,7 @@ Transfer NEAR tokens between accounts. This returns an object with transaction a View functions are read-only functions that don't change the state of the contract. We can call these functions without instantiating an account or a key store. - + ```js import { providers } from 'near-api-js'; @@ -716,7 +740,7 @@ View functions are read-only functions that don't change the state of the contra const result = JSON.parse(Buffer.from(res.result).toString()); ``` - + ```rust use near_jsonrpc_client::{methods, JsonRpcClient}; @@ -776,7 +800,7 @@ View functions are read-only functions that don't change the state of the contra ### Call Function - + ```js import { connect, transactions, keyStores } from "near-api-js"; @@ -795,7 +819,7 @@ View functions are read-only functions that don't change the state of the contra }); ``` - + ```rust use near_crypto::Signer; @@ -910,12 +934,120 @@ View functions are read-only functions that don't change the state of the contra
+### Batch Transactions + + + + + You may batch send transactions by using the `signAndSendTransaction({})` method from `account`. This method takes an array of transaction actions, and if one fails, the entire operation will fail. Here's a simple example: + + ```js + const { connect, transactions, keyStores } = require("near-api-js"); + const fs = require("fs"); + const path = require("path"); + const homedir = require("os").homedir(); + + const CREDENTIALS_DIR = ".near-credentials"; + const CONTRACT_NAME = "spf.idea404.testnet"; + const WASM_PATH = path.join(__dirname, "../build/uninitialized_nft.wasm"); + + const credentialsPath = path.join(homedir, CREDENTIALS_DIR); + const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); + + const config = { + keyStore, + networkId: "testnet", + nodeUrl: "https://rpc.testnet.near.org", + }; + + sendTransactions(); + + async function sendTransactions() { + const near = await connect({ ...config, keyStore }); + const account = await near.account(CONTRACT_NAME); + const args = { some_field: 1, another_field: "hello" }; + + const balanceBefore = await account.getAccountBalance(); + console.log("Balance before:", balanceBefore); + + try { + const result = await account.signAndSendTransaction({ + receiverId: CONTRACT_NAME, + actions: [ + transactions.deployContract(fs.readFileSync(WASM_PATH)), // Contract does not get deployed + transactions.functionCall("new", Buffer.from(JSON.stringify(args)), 10000000000000, "0"), // this call fails + transactions.transfer("1" + "0".repeat(24)), // 1 NEAR is not transferred either + ], + }); + console.log(result); + } catch (e) { + console.log("Error:", e); + } + + const balanceAfter = await account.getAccountBalance(); + console.log("Balance after:", balanceAfter); + } + ``` + +
+ Response Example + ```bash + Balance before: { + total: '49987878054959838200000000', + stateStaked: '4555390000000000000000000', + staked: '0', + available: '45432488054959838200000000' + } + Receipts: 2PPueY6gnA4YmmQUzc8DytNBp4PUpgTDhmEjRSHHVHBd, 3isLCW9SBH1MrPjeEPAmG9saHLj9Z2g7HxzfBdHmaSaG + Failure [spf.idea404.testnet]: Error: {"index":1,"kind":{"ExecutionError":"Smart contract panicked: panicked at 'Failed to deserialize input from JSON.: Error(\"missing field `owner_id`\", line: 1, column: 40)', nft/src/lib.rs:47:1"}} + Error: ServerTransactionError: {"index":1,"kind":{"ExecutionError":"Smart contract panicked: panicked at 'Failed to deserialize input from JSON.: Error(\"missing field `owner_id`\", line: 1, column: 40)', nft/src/lib.rs:47:1"}} + at parseResultError (/Users/dennis/Code/naj-test/node_modules/near-api-js/lib/utils/rpc_errors.js:31:29) + at Account. (/Users/dennis/Code/naj-test/node_modules/near-api-js/lib/account.js:156:61) + at Generator.next () + at fulfilled (/Users/dennis/Code/naj-test/node_modules/near-api-js/lib/account.js:5:58) + at processTicksAndRejections (node:internal/process/task_queues:96:5) { + type: 'FunctionCallError', + context: undefined, + index: 1, + kind: { + ExecutionError: "Smart contract panicked: panicked at \'Failed to deserialize input from JSON.: Error("missing field `owner_id`", line: 1, column: 40)\', nft/src/lib.rs:47:1" + }, + transaction_outcome: { + block_hash: '5SUhYcXjXR1svCxL5BhCuw88XNdEjKXqWgA9X4XZW1dW', + id: 'SKQqAgnSN27fyHpncaX3fCUxWknBrMtxxytWLRDQfT3', + outcome: { + executor_id: 'spf.idea404.testnet', + gas_burnt: 4839199843770, + logs: [], + metadata: [Object], + receipt_ids: [Array], + status: [Object], + tokens_burnt: '483919984377000000000' + }, + proof: [ [Object], [Object], [Object], [Object], [Object] ] + } + } + Balance after: { + total: '49985119959346682700000000', + stateStaked: '4555390000000000000000000', + staked: '0', + available: '45429729959346682700000000' + } + ``` +
+ + You may also find an example of batch transactions in the [Cookbook](/tools/near-api-js/cookbook). +
+
+ +
+ ### Deploy a Contract {#deploy-a-contract} You can deploy a contract from a compiled WASM file. This returns an object with transaction and receipts outcomes and status. - + ```js const account = await nearConnection.account("example-account.testnet"); @@ -935,7 +1067,7 @@ You can get and manage keys for an account. ### Add Function Access Key {#add-function-access-key} - + ```js const account = await nearConnection.account("example-account.testnet"); @@ -955,7 +1087,7 @@ You can get and manage keys for an account. ### Add Full Access Key {#add-full-access-key} - + ```js // takes public key as string for argument @@ -971,7 +1103,7 @@ You can get and manage keys for an account. ### Get All Access Keys {#get-all-access-keys} - + ```js const account = await nearConnection.account("example-account.testnet"); @@ -979,7 +1111,7 @@ You can get and manage keys for an account. ``` - + ```rust use near_jsonrpc_client::methods; @@ -1036,7 +1168,7 @@ You can get and manage keys for an account. ### Delete Access Key {#delete-access-key} - + ```js const account = await nearConnection.account("example-account.testnet"); @@ -1053,7 +1185,7 @@ You can get and manage keys for an account. ### NEAR => yoctoNEAR {#near--yoctonear} - + ```js // converts NEAR amount into yoctoNEAR (10^-24) @@ -1070,7 +1202,7 @@ You can get and manage keys for an account. ### YoctoNEAR => NEAR {#yoctonear--near} - + ```js // converts yoctoNEAR (10^-24) amount into NEAR @@ -1087,7 +1219,7 @@ You can get and manage keys for an account. ## Additional resources - + - [Handling Passphrases](https://github.com/near/near-seed-phrase) - [Type Docs](https://near.github.io/near-api-js) From 83cacb3203b42f9fd69fe076d63be25d4ac90421 Mon Sep 17 00:00:00 2001 From: PiVortex Date: Thu, 28 Nov 2024 14:52:24 +0000 Subject: [PATCH 4/6] add most of rust --- docs/4.tools/near-api.md | 773 ++++++++++++++------------------------- 1 file changed, 272 insertions(+), 501 deletions(-) diff --git a/docs/4.tools/near-api.md b/docs/4.tools/near-api.md index 4862b8f7cfe..00750785909 100644 --- a/docs/4.tools/near-api.md +++ b/docs/4.tools/near-api.md @@ -11,7 +11,7 @@ The NEAR API is a set of libraries that allow you to interact with the NEAR bloc The API is available in multiple languages, including: - JavaScript: [`near-api-js`](https://github.com/near/near-api-js) -- Rust: [`near-jsonrpc-client`](https://github.com/near/near-jsonrpc-client-rs) +- Rust: [`near-api-rs`](https://github.com/near/near-api-rs) - Python: [`py-near`](https://github.com/pvolnov/py-near) For example, you could use [`near-api-js`](https://github.com/near/near-api-js) to create web applications or backend services written in node.js servers. @@ -44,7 +44,7 @@ To allow users to login into your web application using a wallet you will need t ```bash - cargo add near-jsonrpc-client + cargo add near-api ``` @@ -63,9 +63,9 @@ To allow users to login into your web application using a wallet you will need t - Each one of the valid JSON RPC methods are defined in the methods module. + The methods to interact with the NEAR API are available through the `prelude` module. ```rust - use near_jsonrpc_client::methods; + use near_api::prelude::*; ``` @@ -217,11 +217,96 @@ To allow users to login into your web application using a wallet you will need t + Standard connections `mainnet` and `testnet` are available that come with standard configurations for each network. + + ```rust + let network = NetworkConfig::testnet(); + ``` + + You can make the connection mutable to change some details of the connection. + ```rust - use near_jsonrpc_client::JsonRpcClient; + let mut network = NetworkConfig::testnet(); + network.rpc_url = "https://rpc.testnet.near.org".parse().unwrap(); + ``` + + You can also create your own custom connection. - let testnet_client = JsonRpcClient::connect("https://rpc.testnet.near.org"); + ```rust + let network = NetworkConfig { + network_name: "testnet".to_string(), + rpc_url: "https://rpc.testnet.near.org".parse().unwrap(), + rpc_api_key: None, + linkdrop_account_id: Some("testnet".parse().unwrap()), + near_social_db_contract_account_id: Some("v1.social08.testnet".parse().unwrap()), + faucet_url: Some("https://helper.nearprotocol.com/account".parse().unwrap()), + meta_transaction_relayer_url: Some("http://localhost:3030/relay".parse().unwrap()), + fastnear_url: None, + staking_pools_factory_account_id: Some("pool.f863973.m0".parse().unwrap()), + }; ``` + +
+ + #### Signer + + If you're going to sign transactions, you need to create a `signer` object. + + + + + This example uses the Keystore that is also used as the standard for saving keys with the NEAR CLI. + + ```rust + use near_api::signer::keystore::KeystoreSigner; + + let search_keystore_signer = KeystoreSigner::search_for_keys(my_account_id.clone(), &network) + .await + .unwrap(); + let keystore_signer_search = Signer::new(search_keystore_signer).unwrap(); + ``` + + + + + Keys can be loaded from a file that contains a public and private key. This is an example of loading a key from a file that was saved using the legacy option using the NEAR CLI. + + ```rust + use std::env; + use std::path::Path; + + let my_account_id = "example-account.testnet"; + let home_dir = env::var("HOME").unwrap(); + let credentials_dir = Path::new(&home_dir).join(".near-credentials"); + let file_path = credentials_dir.join(format!("testnet/{}.json", my_account_id)); + + let file_signer = Signer::new(Signer::access_keyfile(file_path).unwrap()).unwrap(); + ``` + + + + + ```rust + let seed_phrase = + "shoe three gate jelly whole tissue parrot robust census lens staff ship".to_string(); + let seed_phrase_signer = Signer::new(Signer::seed_phrase(seed_phrase, None).unwrap()).unwrap(); + ``` + + + + + + ```rust + use near_crypto::SecretKey; + use std::str::FromStr; + + let private_key = SecretKey::from_str("ed25519:3bUTUXCPHPbAD5JDukzsWT6AaJ9iZA3FF9wLgYgRvzC7CDYMgmEExtxyGjnGATvmM3oggqUErvRkN9sjzNTD8yd7").unwrap(); + let priv_key_signer = Signer::new(Signer::secret_key(private_key)).unwrap(); + ``` + + + +
@@ -275,13 +360,21 @@ This will return an Account object for you to interact with. const account = await nearConnection.account("example-account.testnet"); ``` + :::warning + In order to be able to use the account, its credentials must be stored in the [key store](#key-store) + ::: + +
+ + + ```rust + let account_id: AccountId = "example-account.testnet".parse().unwrap(); + let account = Account::new(account_id.clone(), network); + ``` +
-:::warning -In order to be able to use the account, its credentials must be stored in the [key store](#key-store) -::: -
### Get Balance {#get-balance} @@ -295,6 +388,18 @@ In order to be able to use the account, its credentials must be stored in the [k const accountBalance = await account.getAccountBalance(); ``` +
+ + + ```rust + let account_id: AccountId = "example-account.testnet".parse().unwrap(); + let account_balance = Tokens::of(account_id.clone()) + .near_balance() + .fetch_from(&network) + .await + .unwrap(); + ``` +
@@ -333,34 +438,10 @@ Get basic account information, such as amount of tokens the account has or the a ```rust - use near_jsonrpc_client::methods; - use near_jsonrpc_primitives::types::query::QueryResponseKind; - use near_primitives::types::{AccountId, BlockReference, Finality}; - use near_primitives::views::QueryRequest; - - mod utils; - - #[tokio::main] - async fn main() -> Result<(), Box> { - env_logger::init(); - - let client = utils::select_network()?; + let account_id: AccountId = "example-account.testnet".parse().unwrap(); + let account = Account::new(my_account_id.clone(), network); - let account_id: AccountId = utils::input("Enter an Account ID to lookup: ")?.parse()?; - - let request = methods::query::RpcQueryRequest { - block_reference: BlockReference::Finality(Finality::Final), - request: QueryRequest::ViewAccount { account_id }, - }; - - let response = client.call(request).await?; - - if let QueryResponseKind::ViewAccount(result) = response.kind { - println!("{:#?}", result); - } - - Ok(()) - } + let account_state = account.view().fetch_from(&network).await.unwrap(); ```
@@ -394,284 +475,60 @@ Create a sub-account. contractId: "testnet", methodName: "create_account", args: { - new_account_id: "new-account.testnet", - new_public_key: "ed25519:2ASWccunZMBSygADWG2pXuHM6jWdnzLzWFU6r7wtaHYt", + new_account_id: "new-account.testnet", + new_public_key: "ed25519:2ASWccunZMBSygADWG2pXuHM6jWdnzLzWFU6r7wtaHYt", }, gas: "300000000000000", attachedDeposit: utils.format.parseNearAmount(amount), - }); + }); ```
+ You will need to create a signer object first. - ```rust - //! Creates an account on the network. - //! - //! Creates either; - //! - a top-level mainnet / testnet account - //! - or a sub-account for any account on the network. - //! - //! top-level account example: `miraclx.near` creates `foobar.near` - //! sub-account example: `miraclx.near` creates `test.miraclx.near` - //! - //! This script is interactive. - - use near_crypto::Signer; - use near_jsonrpc_client::methods::broadcast_tx_commit::RpcTransactionError; - use near_jsonrpc_client::{methods, JsonRpcClient}; - use near_jsonrpc_primitives::types::query::QueryResponseKind; - use near_jsonrpc_primitives::types::transactions::TransactionInfo; - use near_primitives::hash::CryptoHash; - use near_primitives::transaction::{ - Action, AddKeyAction, CreateAccountAction, FunctionCallAction, Transaction, TransactionV0, - TransferAction, - }; - use near_primitives::types::{AccountId, BlockReference}; - use near_primitives::views::{FinalExecutionStatus, TxExecutionStatus}; - - use serde_json::json; - use tokio::time; - - mod utils; - - async fn account_exists( - client: &JsonRpcClient, - account_id: &AccountId, - ) -> Result> { - let access_key_query_response = client - .call(methods::query::RpcQueryRequest { - block_reference: BlockReference::latest(), - request: near_primitives::views::QueryRequest::ViewAccount { - account_id: account_id.clone(), - }, - }) - .await; - - match access_key_query_response { - Ok(_) => Ok(true), - Err(near_jsonrpc_client::errors::JsonRpcError::ServerError( - near_jsonrpc_client::errors::JsonRpcServerError::HandlerError( - near_jsonrpc_primitives::types::query::RpcQueryError::UnknownAccount { .. }, - ), - )) => Ok(false), - Err(res) => Err(res)?, - } - } + This example creates the sub-account and saves the seed phrase of a generated key pair to a file. - async fn get_current_nonce( - client: &JsonRpcClient, - account_id: &AccountId, - public_key: &near_crypto::PublicKey, - ) -> Result, Box> { - let query_response = client - .call(methods::query::RpcQueryRequest { - block_reference: BlockReference::latest(), - request: near_primitives::views::QueryRequest::ViewAccessKey { - account_id: account_id.clone(), - public_key: public_key.clone(), - }, - }) - .await; - - match query_response { - Ok(access_key_query_response) => match access_key_query_response.kind { - QueryResponseKind::AccessKey(access_key) => Ok(Some(( - access_key_query_response.block_hash, - access_key.nonce, - ))), - _ => Err("failed to extract current nonce")?, - }, - Err(near_jsonrpc_client::errors::JsonRpcError::ServerError( - near_jsonrpc_client::errors::JsonRpcServerError::HandlerError( - near_jsonrpc_primitives::types::query::RpcQueryError::UnknownAccessKey { .. }, - ), - )) => Ok(None), - Err(res) => Err(res)?, - } - } - - #[tokio::main] - async fn main() -> Result<(), Box> { - env_logger::init(); - - let client = utils::select_network()?; - - let signer_account_id = loop { - let signer_account_id = utils::input("Enter the creators Account ID: ")?.parse()?; - if account_exists(&client, &signer_account_id).await? { - break signer_account_id; - } - println!("(i) This account doesn't exist, please reenter!"); - }; - - let (signer, latest_hash, current_nonce) = loop { - let signer_secret_key = utils::input("Enter the creators's private key: ")?.parse()?; - - let signer = near_crypto::InMemorySigner::from_secret_key( - signer_account_id.clone(), - signer_secret_key, - ); - - if let Some((latest_hash, current_nonce)) = - get_current_nonce(&client, &signer.account_id, &signer.public_key).await? - { - break (signer, latest_hash, current_nonce); - } - println!("(i) Invalid access key, please reenter!"); - }; - - let new_account_id = loop { - let new_account_id = utils::input("What's the new Account ID: ")?.parse()?; - if !account_exists(&client, &new_account_id).await? { - break new_account_id; - } - println!("(i) This account already exists, please reenter!"); - }; - - let initial_deposit = loop { - let deposit: f64 = - utils::input("How much do you want to fund this account with (in Ⓝ units)? ")? - .parse()?; - if deposit >= 0.0 { - break ((deposit * 1_000_000.0) as u128) * 1_000_000_000_000_000_000_u128; - } - println!("(i) Enter a non-zero deposit value!"); - }; - - let is_sub_account = new_account_id.is_sub_account_of(&signer.account_id); - let new_key_pair = near_crypto::SecretKey::from_random(near_crypto::KeyType::ED25519); - - let (transaction, expected_output) = if is_sub_account { - ( - TransactionV0 { - signer_id: signer.account_id.clone(), - public_key: signer.public_key.clone(), - nonce: current_nonce + 1, - receiver_id: new_account_id.clone(), - block_hash: latest_hash, - actions: vec![ - Action::CreateAccount(CreateAccountAction {}), - Action::AddKey(Box::new(AddKeyAction { - access_key: near_primitives::account::AccessKey { - nonce: 0, - permission: near_primitives::account::AccessKeyPermission::FullAccess, - }, - public_key: new_key_pair.public_key(), - })), - Action::Transfer(TransferAction { - deposit: initial_deposit, - }), - ], - }, - vec![], - ) - } else { - let contract_id = if client.server_addr().ends_with("testnet.near.org") { - "testnet".parse()? - } else if client.server_addr().ends_with("mainnet.near.org") { - "near".parse()? - } else { - Err("can only create non-sub accounts for mainnet / testnet\nconsider creating a sub-account instead")? - }; - ( - TransactionV0 { - signer_id: signer.account_id.clone(), - public_key: signer.public_key.clone(), - nonce: current_nonce + 1, - receiver_id: contract_id, - block_hash: latest_hash, - actions: vec![Action::FunctionCall(Box::new(FunctionCallAction { - method_name: "create_account".to_string(), - args: json!({ - "new_account_id": new_account_id, - "new_public_key": new_key_pair.public_key(), - }) - .to_string() - .into_bytes(), - gas: 300_000_000_000_000, - deposit: initial_deposit, - }))], - }, - b"true".to_vec(), - ) - }; - - println!("============================================================="); - println!("New Account ID: {}", new_account_id); - println!(" Secret Key: {}", new_key_pair); - println!(" Public Key: {}", new_key_pair.public_key()); - println!(" Deposit: {}", initial_deposit); - println!("-------------------------------------------------------------"); - - let request = methods::broadcast_tx_async::RpcBroadcastTxAsyncRequest { - signed_transaction: Transaction::V0(transaction).sign(&Signer::InMemory(signer.clone())), - }; - - let sent_at = time::Instant::now(); - - let tx_hash = client.call(request).await?; - - println!(" Tx Hash: {}", tx_hash); - println!("============================================================="); - - loop { - let response = client - .call(methods::tx::RpcTransactionStatusRequest { - transaction_info: TransactionInfo::TransactionId { - tx_hash, - sender_account_id: signer.account_id.clone(), - }, - wait_until: TxExecutionStatus::Final, - }) - .await; - let received_at = time::Instant::now(); - let delta = (received_at - sent_at).as_secs(); - - if delta > 60 { - Err("time limit exceeded for the transaction to be recognized")?; - } - - match response { - Ok(tx) => { - // it's fine to unwrap because we asked for finalized tx - let outcome = tx.final_execution_outcome.unwrap().into_outcome(); - match outcome.status { - FinalExecutionStatus::Failure(err) => { - println!("{:#?}", err); - println!("(!) Creating the account failed, check above for full logs"); - break; - } - FinalExecutionStatus::SuccessValue(ref s) => { - if s == &expected_output { - println!("(i) Account successfully created after {}s", delta); - } else { - println!("{:#?}", outcome); - println!("(!) Creating the account failed, check above for full logs"); - } - break; - } - _ => {} - } - } - Err(err) => match err.handler_error() { - Some( - RpcTransactionError::TimeoutError - | RpcTransactionError::UnknownTransaction { .. }, - ) => { - time::sleep(time::Duration::from_secs(2)).await; - continue; - } - _ => Err(err)?, - }, - } - } + ```rust + let account_id: AccountId = "example-account.testnet".parse().unwrap(); + let new_account_id: AccountId = "sub.example-account.testnet".parse().unwrap(); + let res = Account::create_account() + .fund_myself( + new_account_id.clone(), // new account id + account_id.clone(), // account id funding the new account + NearToken::from_near(1), // Initial balance for the new account + ) + .new_keypair() // Generates a new random key pair + .save_generated_seed_to_file("./new_account_seed".into()) + .unwrap() + .with_signer(signer.clone()) + .send_to(&network) + .await + .unwrap(); + ``` + + Creating a `.near` or `.testnet` account is the exact same process as creating a sub-account. - Ok(()) - } + ```rust + let account_id: AccountId = "example-account.testnet".parse().unwrap(); + let new_account_id: AccountId = "new_example-account.testnet".parse().unwrap(); + let res = Account::create_account() + .fund_myself( + new_account_id.clone(), // new account id + account_id.clone(), // account id funding the new account + NearToken::from_near(1), // Initial balance for the new account + ) + .new_keypair() // Generates a new random key pair + .save_generated_seed_to_file("./new_account_seed".into()) + .unwrap() + .with_signer(signer.clone()) + .send_to(&network) + .await + .unwrap(); ``` +
@@ -689,6 +546,18 @@ Create a sub-account. await account.deleteAccount("beneficiary-account.testnet"); ``` +
+ + + ```rust + account + .delete_account_with_beneficiary(beneficiary_account_id.clone()) + .with_signer(signer.clone()) + .send_to(&network) + .await + .unwrap(); + ``` +
@@ -711,16 +580,30 @@ Transfer NEAR tokens between accounts. This returns an object with transaction a ); ```
+ + + ```rust + Tokens::of(sender_account_id.clone()) + .send_to(receiver_account_id.clone()) + .near(NearToken::from_near(1)) + .with_signer(signer.clone()) + .send_to(&network) + .await + .unwrap() + .assert_success(); + ``` + +

### View Function -View functions are read-only functions that don't change the state of the contract. We can call these functions without instantiating an account or a key store. - + + View functions are read-only functions that don't change the state of the contract. We can call these functions without instantiating an account or a key store. ```js import { providers } from 'near-api-js'; @@ -741,56 +624,20 @@ View functions are read-only functions that don't change the state of the contra ``` + View functions are read-only functions that don't change the state of the contract. We can call these functions without a signer. - ```rust - use near_jsonrpc_client::{methods, JsonRpcClient}; - use near_jsonrpc_primitives::types::query::QueryResponseKind; - use near_primitives::types::{BlockReference, Finality, FunctionArgs}; - use near_primitives::views::QueryRequest; - - use serde::Deserialize; - use serde_json::{from_slice, json}; - - mod utils; - - #[derive(Debug, Deserialize)] - pub struct AccountStatus { - pub rating: f32, - pub given: u64, - pub received: u64, - } + You need to specify what type the view function returns. - #[tokio::main] - async fn main() -> Result<(), Box> { - env_logger::init(); - - let client = JsonRpcClient::connect("https://rpc.testnet.near.org"); - - let account_id = utils::input("Enter the account to view: ")?; - - let request = methods::query::RpcQueryRequest { - block_reference: BlockReference::Finality(Finality::Final), - request: QueryRequest::CallFunction { - account_id: "nosedive.testnet".parse()?, - method_name: "status".to_string(), - args: FunctionArgs::from( - json!({ - "account_id": account_id, - }) - .to_string() - .into_bytes(), - ), - }, - }; - - let response = client.call(request).await?; - - if let QueryResponseKind::CallResult(result) = response.kind { - println!("{:#?}", from_slice::(&result.result)?); - } + ```rust + let res: Data = contract + .call_function("total_messages", ()) + .unwrap() + .read_only() + .fetch_from(&network) + .await + .unwrap(); - Ok(()) - } + println!("{:?}", res.data); ``` @@ -802,6 +649,8 @@ View functions are read-only functions that don't change the state of the contra + A call function changes the contract's state and does require an account. + ```js import { connect, transactions, keyStores } from "near-api-js"; @@ -821,113 +670,22 @@ View functions are read-only functions that don't change the state of the contra + A call function changes the contract's state and does require a signer. ```rust - use near_crypto::Signer; - use near_jsonrpc_client::{methods, JsonRpcClient}; - use near_jsonrpc_primitives::types::query::QueryResponseKind; - use near_jsonrpc_primitives::types::transactions::{RpcTransactionError, TransactionInfo}; - use near_primitives::transaction::{Action, FunctionCallAction, Transaction, TransactionV0}; - use near_primitives::types::BlockReference; - use near_primitives::views::TxExecutionStatus; - - use serde_json::json; - use tokio::time; - - mod utils; - - #[tokio::main] - async fn main() -> Result<(), Box> { - env_logger::init(); - - let client = JsonRpcClient::connect("https://rpc.testnet.near.org"); - - let signer_account_id = utils::input("Enter the signer Account ID: ")?.parse()?; - let signer_secret_key = utils::input("Enter the signer's private key: ")?.parse()?; - - let signer = near_crypto::InMemorySigner::from_secret_key(signer_account_id, signer_secret_key); - - let access_key_query_response = client - .call(methods::query::RpcQueryRequest { - block_reference: BlockReference::latest(), - request: near_primitives::views::QueryRequest::ViewAccessKey { - account_id: signer.account_id.clone(), - public_key: signer.public_key.clone(), - }, - }) - .await?; - - let current_nonce = match access_key_query_response.kind { - QueryResponseKind::AccessKey(access_key) => access_key.nonce, - _ => Err("failed to extract current nonce")?, - }; - - let other_account = utils::input("Enter the account to be rated: ")?; - let rating = utils::input("Enter a rating: ")?.parse::()?; - - let transaction = TransactionV0 { - signer_id: signer.account_id.clone(), - public_key: signer.public_key.clone(), - nonce: current_nonce + 1, - receiver_id: "nosedive.testnet".parse()?, - block_hash: access_key_query_response.block_hash, - actions: vec![Action::FunctionCall(Box::new(FunctionCallAction { - method_name: "rate".to_string(), - args: json!({ - "account_id": other_account, - "rating": rating, - }) - .to_string() - .into_bytes(), - gas: 100_000_000_000_000, // 100 TeraGas - deposit: 0, - }))], - }; - - let request = methods::broadcast_tx_async::RpcBroadcastTxAsyncRequest { - signed_transaction: Transaction::V0(transaction).sign(&Signer::InMemory(signer.clone())), - }; - - let sent_at = time::Instant::now(); - let tx_hash = client.call(request).await?; - - loop { - let response = client - .call(methods::tx::RpcTransactionStatusRequest { - transaction_info: TransactionInfo::TransactionId { - tx_hash, - sender_account_id: signer.account_id.clone(), - }, - wait_until: TxExecutionStatus::Executed, - }) - .await; - let received_at = time::Instant::now(); - let delta = (received_at - sent_at).as_secs(); - - if delta > 60 { - Err("time limit exceeded for the transaction to be recognized")?; - } - - match response { - Err(err) => match err.handler_error() { - Some( - RpcTransactionError::TimeoutError - | RpcTransactionError::UnknownTransaction { .. }, - ) => { - time::sleep(time::Duration::from_secs(2)).await; - continue; - } - _ => Err(err)?, - }, - Ok(response) => { - println!("response gotten after: {}s", delta); - println!("response: {:#?}", response); - break; - } - } - } + let args = json!({ + "text": "Hello, world!" + }); - Ok(()) - } + contract + .call_function("add_message", args) + .unwrap() + .transaction() + .deposit(NearToken::from_near(1)) + .with_signer(my_account_id.clone(), signer.clone()) + .send_to(&network) + .await + .unwrap() + .assert_success(); ``` @@ -1058,6 +816,12 @@ You can deploy a contract from a compiled WASM file. This returns an object with
+
+ +### Signing Messages + + + --- ## Keys @@ -1079,6 +843,30 @@ You can get and manage keys for an account. ); ``` +
+ + + ```rust + use near_primitives::account::FunctionCallPermission; + + let new_function_call_key = AccessKeyPermission::FunctionCall(FunctionCallPermission { + allowance: Some(250_000_000_000_000_000_000_000), // Allowance this key is allowed to call (optional) + receiver_id: "example-account.testnet".to_string(), // Contract this key is allowed to call + method_names: vec!["example_method".to_string()], // Methods this key is allowed to call + }); + + let (new_private_key, txn) = Account(account_id.clone()) + .add_key(new_function_call_key) + .new_keypair() + .generate_secret_key() // Generates a new keypair via private key + .unwrap(); + + println!("New private key: {:?}", new_private_key.to_string()); + println!("New public key: {:?}", new_private_key.public_key().to_string()); + + txn.with_signer(signer.clone()).send_to(&network).await.unwrap(); // Sends the transaction to the network + ``` +
@@ -1095,6 +883,24 @@ You can get and manage keys for an account. await account.addKey("8hSHprDq2StXwMtNd43wDTXQYsjXcD4MJTXQYsjXcc"); ``` +
+ + + ```rust + use near_primitives::account::AccessKeyPermission; + + let (new_private_key, txn) = Account(account_id.clone()) + .add_key(AccessKeyPermission::FullAccess) + .new_keypair() + .generate_secret_key() // Generates a new keypair via private key + .unwrap(); + + println!("New private key: {:?}", new_private_key.to_string()); + println!("New public key: {:?}", new_private_key.public_key().to_string()); + + txn.with_signer(signer.clone()).send_to(&network).await.unwrap(); + ``` +
@@ -1114,51 +920,8 @@ You can get and manage keys for an account. ```rust - use near_jsonrpc_client::methods; - use near_jsonrpc_primitives::types::query::QueryResponseKind; - use near_primitives::types::BlockReference; - - mod utils; - - fn indent(indentation: usize, s: String) -> String { - let mut lines = s.split_inclusive("\n"); - let mut r = lines.next().unwrap().to_string(); - for l in lines { - r.push_str(&" ".repeat(indentation - 3)); - r.push_str("\x1b[38;5;244m>\x1b[0m "); - r.push_str(l); - } - r - } - - #[tokio::main] - async fn main() -> Result<(), Box> { - env_logger::init(); - - let client = utils::select_network()?; - - let account_id = utils::input("Enter the Account ID whose keys we're listing: ")?.parse()?; - - let access_key_query_response = client - .call(methods::query::RpcQueryRequest { - block_reference: BlockReference::latest(), - request: near_primitives::views::QueryRequest::ViewAccessKeyList { account_id }, - }) - .await?; - - if let QueryResponseKind::AccessKeyList(response) = access_key_query_response.kind { - for access_key in response.keys { - println!("🗝 [{}]", access_key.public_key); - println!(" \u{21b3} nonce: {}", access_key.access_key.nonce); - println!( - " \u{21b3} permission: {}", - indent(20, format!("{:#?}", access_key.access_key.permission)) - ); - } - } - - Ok(()) - } + let account = Account(account_id.clone()); + let keys = account.list_keys().fetch_from(&network).await.unwrap(); ```
@@ -1175,6 +938,14 @@ You can get and manage keys for an account. await account.deleteKey("8hSHprDq2StXwMtNd43wDTXQYsjXcD4MJTXQYsjXcc"); ``` +
+ + + ```rust + let my_account = Account(account_id.clone()); + my_account.delete_key(new_private_key.public_key()).with_signer(signer.clone()).send_to(&network).await.unwrap(); + ``` +
From ca654528e77b842baea840953d0757a2601d0487 Mon Sep 17 00:00:00 2001 From: PiVortex Date: Fri, 29 Nov 2024 13:42:36 +0000 Subject: [PATCH 5/6] api additions --- docs/4.tools/near-api.md | 196 ++++++++++++++++++++++++--------------- 1 file changed, 122 insertions(+), 74 deletions(-) diff --git a/docs/4.tools/near-api.md b/docs/4.tools/near-api.md index 00750785909..ec5d801e535 100644 --- a/docs/4.tools/near-api.md +++ b/docs/4.tools/near-api.md @@ -263,7 +263,7 @@ To allow users to login into your web application using a wallet you will need t let search_keystore_signer = KeystoreSigner::search_for_keys(my_account_id.clone(), &network) .await .unwrap(); - let keystore_signer_search = Signer::new(search_keystore_signer).unwrap(); + let signer = Signer::new(search_keystore_signer).unwrap(); ```
@@ -275,12 +275,12 @@ To allow users to login into your web application using a wallet you will need t use std::env; use std::path::Path; - let my_account_id = "example-account.testnet"; + let account_id = "example-account.testnet"; let home_dir = env::var("HOME").unwrap(); let credentials_dir = Path::new(&home_dir).join(".near-credentials"); - let file_path = credentials_dir.join(format!("testnet/{}.json", my_account_id)); + let file_path = credentials_dir.join(format!("testnet/{}.json", account_id)); - let file_signer = Signer::new(Signer::access_keyfile(file_path).unwrap()).unwrap(); + let signer = Signer::new(Signer::access_keyfile(file_path).unwrap()).unwrap(); ```
@@ -289,7 +289,7 @@ To allow users to login into your web application using a wallet you will need t ```rust let seed_phrase = "shoe three gate jelly whole tissue parrot robust census lens staff ship".to_string(); - let seed_phrase_signer = Signer::new(Signer::seed_phrase(seed_phrase, None).unwrap()).unwrap(); + let signer = Signer::new(Signer::seed_phrase(seed_phrase, None).unwrap()).unwrap(); ```
@@ -301,7 +301,7 @@ To allow users to login into your web application using a wallet you will need t use std::str::FromStr; let private_key = SecretKey::from_str("ed25519:3bUTUXCPHPbAD5JDukzsWT6AaJ9iZA3FF9wLgYgRvzC7CDYMgmEExtxyGjnGATvmM3oggqUErvRkN9sjzNTD8yd7").unwrap(); - let priv_key_signer = Signer::new(Signer::secret_key(private_key)).unwrap(); + let signer = Signer::new(Signer::secret_key(private_key)).unwrap(); ```
@@ -369,7 +369,7 @@ This will return an Account object for you to interact with. ```rust let account_id: AccountId = "example-account.testnet".parse().unwrap(); - let account = Account::new(account_id.clone(), network); + let account = Account(account_id.clone()); ```
@@ -439,7 +439,7 @@ Get basic account information, such as amount of tokens the account has or the a ```rust let account_id: AccountId = "example-account.testnet".parse().unwrap(); - let account = Account::new(my_account_id.clone(), network); + let account = Account(my_account_id.clone()); let account_state = account.view().fetch_from(&network).await.unwrap(); ``` @@ -448,13 +448,61 @@ Get basic account information, such as amount of tokens the account has or the a
-### Create Sub-Account {#create-account} +### Create an Account {#create-account} + + + + + In order to create .near or .testnet accounts, you need to make a function call to the top-level-domain (i.e. `near` or `testnet`), calling `create_account`: + + ```js + return await creatorAccount.functionCall({ + contractId: "testnet", + methodName: "create_account", + args: { + new_account_id: "new-account.testnet", + new_public_key: "ed25519:2ASWccunZMBSygADWG2pXuHM6jWdnzLzWFU6r7wtaHYt", + }, + gas: "300000000000000", + attachedDeposit: utils.format.parseNearAmount(amount), + }); + ``` + + + + + In this example when we create the account we generate a seed phrase for the new account and save it to a file. + + ```rust + let account_id: AccountId = "example-account.testnet".parse().unwrap(); + let new_account_id: AccountId = "new_example-account.testnet".parse().unwrap(); + let res = Account::create_account() + .fund_myself( + new_account_id.clone(), // new account id + account_id.clone(), // account id funding the new account + NearToken::from_near(1), // Initial balance for the new account + ) + .new_keypair() // Generates a new random key pair + .save_generated_seed_to_file("./new_account_seed".into()) + .unwrap() + .with_signer(signer.clone()) + .send_to(&network) + .await + .unwrap(); + ``` + + + + +
-Create a sub-account. +### Create a Sub-Account {#create-sub-account} + For creating a sub-account there is a specific method that is used. + ```js // creates a sub-account using funds from the account used to create it. const account = await nearConnection.account("example-account.testnet"); @@ -465,68 +513,27 @@ Create a sub-account. ); ``` -
- Creating .near or .testnet accounts - - In order to create .near or .testnet accounts, you need to make a function call to the top-level-domain (i.e. `near` or `testnet`), calling `create_account`: - - ```js - return await creatorAccount.functionCall({ - contractId: "testnet", - methodName: "create_account", - args: { - new_account_id: "new-account.testnet", - new_public_key: "ed25519:2ASWccunZMBSygADWG2pXuHM6jWdnzLzWFU6r7wtaHYt", - }, - gas: "300000000000000", - attachedDeposit: utils.format.parseNearAmount(amount), - }); - ``` - -
-
- You will need to create a signer object first. - This example creates the sub-account and saves the seed phrase of a generated key pair to a file. + The process for creating a sub-account is the same as creating a new account, but with an account id that is a sub-account of the parent account. ```rust let account_id: AccountId = "example-account.testnet".parse().unwrap(); let new_account_id: AccountId = "sub.example-account.testnet".parse().unwrap(); let res = Account::create_account() - .fund_myself( - new_account_id.clone(), // new account id - account_id.clone(), // account id funding the new account - NearToken::from_near(1), // Initial balance for the new account - ) - .new_keypair() // Generates a new random key pair - .save_generated_seed_to_file("./new_account_seed".into()) - .unwrap() - .with_signer(signer.clone()) - .send_to(&network) - .await - .unwrap(); - ``` - - Creating a `.near` or `.testnet` account is the exact same process as creating a sub-account. - - ```rust - let account_id: AccountId = "example-account.testnet".parse().unwrap(); - let new_account_id: AccountId = "new_example-account.testnet".parse().unwrap(); - let res = Account::create_account() - .fund_myself( - new_account_id.clone(), // new account id - account_id.clone(), // account id funding the new account - NearToken::from_near(1), // Initial balance for the new account - ) - .new_keypair() // Generates a new random key pair - .save_generated_seed_to_file("./new_account_seed".into()) - .unwrap() - .with_signer(signer.clone()) - .send_to(&network) - .await - .unwrap(); + .fund_myself( + new_account_id.clone(), // new account id + account_id.clone(), // account id funding the new account + NearToken::from_near(1), // Initial balance for the new account + ) + .new_keypair() // Generates a new random key pair + .save_generated_seed_to_file("./new_account_seed".into()) + .unwrap() + .with_signer(signer.clone()) + .send_to(&network) + .await + .unwrap(); ``` @@ -629,6 +636,9 @@ Transfer NEAR tokens between accounts. This returns an object with transaction a You need to specify what type the view function returns. ```rust + let contract_id: AccountId = "example-contract.testnet".parse().unwrap(); + let contract = Contract(contract_id.clone()); + let res: Data = contract .call_function("total_messages", ()) .unwrap() @@ -672,6 +682,9 @@ Transfer NEAR tokens between accounts. This returns an object with transaction a A call function changes the contract's state and does require a signer. ```rust + let contract_id: AccountId = "example-contract.testnet".parse().unwrap(); + let contract = Contract(contract_id.clone()); + let args = json!({ "text": "Hello, world!" }); @@ -692,12 +705,12 @@ Transfer NEAR tokens between accounts. This returns an object with transaction a
-### Batch Transactions +### Batch Actions - You may batch send transactions by using the `signAndSendTransaction({})` method from `account`. This method takes an array of transaction actions, and if one fails, the entire operation will fail. Here's a simple example: + You may batch send actions by using the `signAndSendTransaction({})` method from `account`. This method takes an array of transaction actions, and if one fails, the entire operation will fail. Here's a simple example: ```js const { connect, transactions, keyStores } = require("near-api-js"); @@ -718,9 +731,9 @@ Transfer NEAR tokens between accounts. This returns an object with transaction a nodeUrl: "https://rpc.testnet.near.org", }; - sendTransactions(); + sendBatchActions(); - async function sendTransactions() { + async function sendBatchActions() { const near = await connect({ ...config, keyStore }); const account = await near.account(CONTRACT_NAME); const args = { some_field: 1, another_field: "hello" }; @@ -796,6 +809,29 @@ Transfer NEAR tokens between accounts. This returns an object with transaction a You may also find an example of batch transactions in the [Cookbook](/tools/near-api-js/cookbook). + + + ```rust + let function_call_action = Action::FunctionCall(Box::new(FunctionCallAction { + method_name: "increment".to_string(), + args: vec![], + gas: 30_000_000_000_000, + deposit: 0, + })); // Create a function call action + let transfer_action = Action::Transfer(TransferAction { + deposit: 1_000_000_000_000_000_000_000_000, + }); // Create a transfer action + let actions = vec![function_call_action, transfer_action]; + + Transaction::construct(account_id.clone(), receiver_id.clone()) + .add_actions(actions) + .with_signer(signer) + .send_to(&network) + .await + .unwrap() + .assert_success(); + ``` +
@@ -814,13 +850,25 @@ You can deploy a contract from a compiled WASM file. This returns an object with ); ```
-
- -
+ -### Signing Messages + Note that the `signer` here needs to be a signer for the same `account_id` as the one used to construct the `Contract` object. + ```rust + let new_contract_id: AccountId = "new-contract.testnet".parse().unwrap(); + let contract = Contract(new_contract_id.clone()); + new_contract + .deploy(include_bytes!("../contracts/contract.wasm").to_vec()) + .without_init_call() // Can also be .with_init_call() + .with_signer(signer) + .send_to(&network) + .await + .unwrap() + .assert_success(); + ``` + +
--- @@ -942,8 +990,8 @@ You can get and manage keys for an account. ```rust - let my_account = Account(account_id.clone()); - my_account.delete_key(new_private_key.public_key()).with_signer(signer.clone()).send_to(&network).await.unwrap(); + let account = Account(account_id.clone()); + account.delete_key(new_private_key.public_key()).with_signer(signer.clone()).send_to(&network).await.unwrap(); ``` From 32c51ef1ec12e679debff72003a3d3b30744465e Mon Sep 17 00:00:00 2001 From: PiVortex Date: Fri, 29 Nov 2024 13:46:54 +0000 Subject: [PATCH 6/6] quick fix --- docs/4.tools/near-api.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/4.tools/near-api.md b/docs/4.tools/near-api.md index ec5d801e535..4d3c0205878 100644 --- a/docs/4.tools/near-api.md +++ b/docs/4.tools/near-api.md @@ -811,7 +811,12 @@ Transfer NEAR tokens between accounts. This returns an object with transaction a
+ You can send actions in a batch to a single receiver. If one action fails then the entire batch of actions will be reverted. + ```rust + use near_primitives::transaction::Action; + use near_primitives::action::{FunctionCallAction, TransferAction}; + let function_call_action = Action::FunctionCall(Box::new(FunctionCallAction { method_name: "increment".to_string(), args: vec![],