diff --git a/docs/src/developing/clients/javascript-api.md b/docs/src/developing/clients/javascript-api.md index 66b05b5a66..9fa1e06bd4 100644 --- a/docs/src/developing/clients/javascript-api.md +++ b/docs/src/developing/clients/javascript-api.md @@ -4,19 +4,19 @@ title: Web3 JavaScript API ## What is Solana-Web3.js? -The Solana-Web3.js library aims to provide complete coverage of Solana. The library was built on top of the [Solana JSON RPC API](https://docs.solana.com/developing/clients/jsonrpc-api). +The Solana-Web3.js library aims to provide complete coverage of Solana. The library was built on top of the [Solana JSON RPC API](../clients/jsonrpc-api.md). You can find the full documentation for the `@solana/web3.js` library [here](https://solana-labs.github.io/solana-web3.js/). ## Common Terminology -| Term | Definition | -|-------------|------------------------| -| Program | Stateless executable code written to interpret instructions. Programs are capable of performing actions based on the instructions provided. | +| Term | Definition | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Program | Stateless executable code written to interpret instructions. Programs are capable of performing actions based on the instructions provided. | | Instruction | The smallest unit of a program that a client can include in a transaction. Within its processing code, an instruction may contain one or more cross-program invocations. | -| Transaction | One or more instructions signed by the client using one or more Keypairs and executed atomically with only two possible outcomes: success or failure. | +| Transaction | One or more instructions signed by the client using one or more Keypairs and executed atomically with only two possible outcomes: success or failure. | -For the full list of terms, see [Solana terminology](https://docs.solana.com/terminology#cross-program-invocation) +For the full list of terms, see [Solana terminology](../../terminology#cross-program-invocation) ## Getting Started @@ -49,19 +49,17 @@ $ npm install --save @solana/web3.js #### Javascript ```javascript -const solanaWeb3 = require('@solana/web3.js'); +const solanaWeb3 = require("@solana/web3.js"); console.log(solanaWeb3); ``` - #### ES6 ```javascript -import * as solanaWeb3 from '@solana/web3.js'; +import * as solanaWeb3 from "@solana/web3.js"; console.log(solanaWeb3); ``` - #### Browser Bundle ```javascript @@ -76,13 +74,14 @@ console.log(solanaWeb3); To allow users to use your dApp or application on Solana, they will need to get access to their Keypair. A Keypair is a private key with a matching public key, used to sign transactions. There are two ways to obtain a Keypair: + 1. Generate a new Keypair 2. Obtain a Keypair using the secret key You can obtain a new Keypair with the following: ```javascript -const {Keypair} = require("@solana/web3.js"); +const { Keypair } = require("@solana/web3.js"); let keypair = Keypair.generate(); ``` @@ -92,15 +91,13 @@ This will generate a brand new Keypair for a user to fund and use within your ap You can allow entry of the secretKey using a textbox, and obtain the Keypair with `Keypair.fromSecretKey(secretKey)`. ```javascript -const {Keypair} = require("@solana/web3.js"); +const { Keypair } = require("@solana/web3.js"); let secretKey = Uint8Array.from([ - 202, 171, 192, 129, 150, 189, 204, 241, 142, 71, 205, - 2, 81, 97, 2, 176, 48, 81, 45, 1, 96, 138, - 220, 132, 231, 131, 120, 77, 66, 40, 97, 172, 91, - 245, 84, 221, 157, 190, 9, 145, 176, 130, 25, 43, - 72, 107, 190, 229, 75, 88, 191, 136, 7, 167, 109, - 91, 170, 164, 186, 15, 142, 36, 12, 23 + 202, 171, 192, 129, 150, 189, 204, 241, 142, 71, 205, 2, 81, 97, 2, 176, 48, + 81, 45, 1, 96, 138, 220, 132, 231, 131, 120, 77, 66, 40, 97, 172, 91, 245, 84, + 221, 157, 190, 9, 145, 176, 130, 25, 43, 72, 107, 190, 229, 75, 88, 191, 136, + 7, 167, 109, 91, 170, 164, 186, 15, 142, 36, 12, 23, ]); let keypair = Keypair.fromSecretKey(secretKey); @@ -117,7 +114,12 @@ A transaction in Solana-Web3.js is created using the [`Transaction`](javascript- Take the example of a transfer transaction: ```javascript -const {Keypair, Transaction, SystemProgram, LAMPORTS_PER_SOL} = require("@solana/web3.js"); +const { + Keypair, + Transaction, + SystemProgram, + LAMPORTS_PER_SOL, +} = require("@solana/web3.js"); let fromKeypair = Keypair.generate(); let toKeypair = Keypair.generate(); @@ -127,8 +129,8 @@ transaction.add( SystemProgram.transfer({ fromPubkey: fromKeypair.publicKey, toPubkey: toKeypair.publicKey, - lamports: LAMPORTS_PER_SOL - }) + lamports: LAMPORTS_PER_SOL, + }), ); ``` @@ -137,16 +139,16 @@ The above code achieves creating a transaction ready to be signed and broadcaste All that is left is to sign the transaction with keypair and send it over the network. You can accomplish sending a transaction by using `sendAndConfirmTransaction` if you wish to alert the user or do something after a transaction is finished, or use `sendTransaction` if you don't need to wait for the transaction to be confirmed. ```javascript -const {sendAndConfirmTransaction, clusterApiUrl, Connection} = require("@solana/web3.js"); +const { + sendAndConfirmTransaction, + clusterApiUrl, + Connection, +} = require("@solana/web3.js"); let keypair = Keypair.generate(); -let connection = new Connection(clusterApiUrl('testnet')); +let connection = new Connection(clusterApiUrl("testnet")); -sendAndConfirmTransaction( - connection, - transaction, - [keypair] -); +sendAndConfirmTransaction(connection, transaction, [keypair]); ``` The above code takes in a `TransactionInstruction` using `SystemProgram`, creates a `Transaction`, and sends it over the network. You use `Connection` in order to define which Solana network you are connecting to, namely `mainnet-beta`, `testnet`, or `devnet`. @@ -175,7 +177,7 @@ Let's look at how to call this instruction using solana-web3.js: ```javascript let keypair = web3.Keypair.generate(); let payer = web3.Keypair.generate(); -let connection = new web3.Connection(web3.clusterApiUrl('testnet')); +let connection = new web3.Connection(web3.clusterApiUrl("testnet")); let airdropSignature = await connection.requestAirdrop( payer.publicKey, @@ -189,9 +191,9 @@ First, we set up the account Keypair and connection so that we have an account t ```javascript let allocateTransaction = new web3.Transaction({ - feePayer: payer.publicKey - }); -let keys = [{pubkey: keypair.publicKey, isSigner: true, isWritable: true}]; + feePayer: payer.publicKey, +}); +let keys = [{ pubkey: keypair.publicKey, isSigner: true, isWritable: true }]; let params = { space: 100 }; ``` @@ -200,10 +202,7 @@ We create the transaction `allocateTransaction`, keys, and params objects. `feeP ```javascript let allocateStruct = { index: 8, - layout: struct([ - u32('instruction'), - ns64('space'), - ]) + layout: struct([u32("instruction"), ns64("space")]), }; ``` @@ -270,20 +269,25 @@ The `layout` in the allocate struct must always have `u32('instruction')` first ```javascript let data = Buffer.alloc(allocateStruct.layout.span); -let layoutFields = Object.assign({instruction: allocateStruct.index}, params); +let layoutFields = Object.assign({ instruction: allocateStruct.index }, params); allocateStruct.layout.encode(layoutFields, data); ``` Using the previously created bufferLayout, we can allocate a data buffer. We then assign our params `{ space: 100 }` so that it maps correctly to the layout, and encode it to the data buffer. Now the data is ready to be sent to the program. ```javascript -allocateTransaction.add(new web3.TransactionInstruction({ - keys, - programId: web3.SystemProgram.programId, - data, -})); +allocateTransaction.add( + new web3.TransactionInstruction({ + keys, + programId: web3.SystemProgram.programId, + data, + }), +); -await web3.sendAndConfirmTransaction(connection, allocateTransaction, [payer, keypair]); +await web3.sendAndConfirmTransaction(connection, allocateTransaction, [ + payer, + keypair, +]); ``` Finally, we add the transaction instruction with all the account keys, payer, data, and programId and broadcast the transaction to the network. @@ -291,14 +295,14 @@ Finally, we add the transaction instruction with all the account keys, payer, da The full code can be found below. ```javascript -const {struct, u32, ns64} = require("@solana/buffer-layout"); -const {Buffer} = require('buffer'); +const { struct, u32, ns64 } = require("@solana/buffer-layout"); +const { Buffer } = require("buffer"); const web3 = require("@solana/web3.js"); let keypair = web3.Keypair.generate(); let payer = web3.Keypair.generate(); -let connection = new web3.Connection(web3.clusterApiUrl('testnet')); +let connection = new web3.Connection(web3.clusterApiUrl("testnet")); let airdropSignature = await connection.requestAirdrop( payer.publicKey, @@ -308,28 +312,30 @@ let airdropSignature = await connection.requestAirdrop( await connection.confirmTransaction(airdropSignature); let allocateTransaction = new web3.Transaction({ - feePayer: payer.publicKey + feePayer: payer.publicKey, }); -let keys = [{pubkey: keypair.publicKey, isSigner: true, isWritable: true}]; +let keys = [{ pubkey: keypair.publicKey, isSigner: true, isWritable: true }]; let params = { space: 100 }; let allocateStruct = { index: 8, - layout: struct([ - u32('instruction'), - ns64('space'), - ]) + layout: struct([u32("instruction"), ns64("space")]), }; let data = Buffer.alloc(allocateStruct.layout.span); -let layoutFields = Object.assign({instruction: allocateStruct.index}, params); +let layoutFields = Object.assign({ instruction: allocateStruct.index }, params); allocateStruct.layout.encode(layoutFields, data); -allocateTransaction.add(new web3.TransactionInstruction({ - keys, - programId: web3.SystemProgram.programId, - data, -})); +allocateTransaction.add( + new web3.TransactionInstruction({ + keys, + programId: web3.SystemProgram.programId, + data, + }), +); -await web3.sendAndConfirmTransaction(connection, allocateTransaction, [payer, keypair]); +await web3.sendAndConfirmTransaction(connection, allocateTransaction, [ + payer, + keypair, +]); ``` diff --git a/docs/src/developing/clients/javascript-reference.md b/docs/src/developing/clients/javascript-reference.md index 57612c87b3..60cde2d553 100644 --- a/docs/src/developing/clients/javascript-reference.md +++ b/docs/src/developing/clients/javascript-reference.md @@ -4,7 +4,7 @@ title: Web3 API Reference ## Web3 API Reference Guide -The `@solana/web3.js` library is a package that has coverage over the [Solana JSON RPC API](https://docs.solana.com/developing/clients/jsonrpc-api). +The `@solana/web3.js` library is a package that has coverage over the [Solana JSON RPC API](../clients/jsonrpc-api.md). You can find the full documentation for the `@solana/web3.js` library [here](https://solana-labs.github.io/solana-web3.js/). @@ -14,7 +14,7 @@ You can find the full documentation for the `@solana/web3.js` library [here](htt [Source Documentation](https://solana-labs.github.io/solana-web3.js/classes/Connection.html) -Connection is used to interact with the [Solana JSON RPC](https://docs.solana.com/developing/clients/jsonrpc-api). You can use Connection to confirm transactions, get account info, and more. +Connection is used to interact with the [Solana JSON RPC](../clients/jsonrpc-api.md). You can use Connection to confirm transactions, get account info, and more. You create a connection by defining the JSON RPC cluster endpoint and the desired commitment. Once this is complete, you can use this connection object to interact with any of the Solana JSON RPC API. @@ -23,7 +23,7 @@ You create a connection by defining the JSON RPC cluster endpoint and the desire ```javascript const web3 = require("@solana/web3.js"); -let connection = new web3.Connection(web3.clusterApiUrl('devnet'), 'confirmed'); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); let slot = await connection.getSlot(); console.log(slot); @@ -64,16 +64,16 @@ A transaction is used to interact with programs on the Solana blockchain. These #### Example Usage ```javascript -const web3 = require('@solana/web3.js'); -const nacl = require('tweetnacl'); +const web3 = require("@solana/web3.js"); +const nacl = require("tweetnacl"); // Airdrop SOL for paying transactions let payer = web3.Keypair.generate(); -let connection = new web3.Connection(web3.clusterApiUrl('devnet'), 'confirmed'); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); let airdropSignature = await connection.requestAirdrop( - payer.publicKey, - web3.LAMPORTS_PER_SOL, + payer.publicKey, + web3.LAMPORTS_PER_SOL, ); await connection.confirmTransaction(airdropSignature); @@ -84,27 +84,31 @@ let toAccount = web3.Keypair.generate(); let transaction = new web3.Transaction(); // Add an instruction to execute -transaction.add(web3.SystemProgram.transfer({ +transaction.add( + web3.SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: toAccount.publicKey, lamports: 1000, -})); + }), +); // Send and confirm transaction // Note: feePayer is by default the first signer, or payer, if the parameter is not set -await web3.sendAndConfirmTransaction(connection, transaction, [payer]) +await web3.sendAndConfirmTransaction(connection, transaction, [payer]); // Alternatively, manually construct the transaction let recentBlockhash = await connection.getRecentBlockhash(); let manualTransaction = new web3.Transaction({ - recentBlockhash: recentBlockhash.blockhash, - feePayer: payer.publicKey + recentBlockhash: recentBlockhash.blockhash, + feePayer: payer.publicKey, }); -manualTransaction.add(web3.SystemProgram.transfer({ +manualTransaction.add( + web3.SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: toAccount.publicKey, lamports: 1000, -})); + }), +); let transactionBuffer = manualTransaction.serializeMessage(); let signature = nacl.sign.detached(transactionBuffer, payer.secretKey); @@ -112,7 +116,7 @@ let signature = nacl.sign.detached(transactionBuffer, payer.secretKey); manualTransaction.addSignature(payer.publicKey, signature); let isVerifiedSignature = manualTransaction.verifySignatures(); -console.log(`The signatures were verifed: ${isVerifiedSignature}`) +console.log(`The signatures were verifed: ${isVerifiedSignature}`); // The signatures were verified: true @@ -130,7 +134,7 @@ The keypair is used to create an account with a public key and secret key within #### Example Usage ```javascript -const {Keypair} = require("@solana/web3.js") +const { Keypair } = require("@solana/web3.js"); let account = Keypair.generate(); @@ -147,8 +151,10 @@ console.log(account.secretKey); // 205, 189, 165, 112, 32, 200, 116, 164, 234 // ] - -let seed = Uint8Array.from([70,60,102,100,70,60,102,100,70,60,102,100,70,60,102,100,70,60,102,100,70,60,102,100,70,60,102,100,70,60,102,100]); +let seed = Uint8Array.from([ + 70, 60, 102, 100, 70, 60, 102, 100, 70, 60, 102, 100, 70, 60, 102, 100, 70, + 60, 102, 100, 70, 60, 102, 100, 70, 60, 102, 100, 70, 60, 102, 100, +]); let accountFromSeed = Keypair.fromSeed(seed); console.log(accountFromSeed.publicKey.toBase58()); @@ -164,7 +170,6 @@ console.log(accountFromSeed.secretKey); // 227, 60, 72, 215, 47, 208, 209, 162, 59 // ] - let accountFromSecret = Keypair.fromSecretKey(account.secretKey); console.log(accountFromSecret.publicKey.toBase58()); @@ -196,25 +201,33 @@ A PublicKey can be created with a base58 encoded string, buffer, Uint8Array, num #### Example Usage ```javascript -const {Buffer} = require('buffer'); -const web3 = require('@solana/web3.js'); -const crypto = require('crypto'); +const { Buffer } = require("buffer"); +const web3 = require("@solana/web3.js"); +const crypto = require("crypto"); // Create a PublicKey with a base58 encoded string -let base58publicKey = new web3.PublicKey('5xot9PVkphiX2adznghwrAuxGs2zeWisNSxMW6hU6Hkj'); +let base58publicKey = new web3.PublicKey( + "5xot9PVkphiX2adznghwrAuxGs2zeWisNSxMW6hU6Hkj", +); console.log(base58publicKey.toBase58()); // 5xot9PVkphiX2adznghwrAuxGs2zeWisNSxMW6hU6Hkj // Create a Program Address let highEntropyBuffer = crypto.randomBytes(31); -let programAddressFromKey = await web3.PublicKey.createProgramAddress([highEntropyBuffer.slice(0, 31)], base58publicKey); +let programAddressFromKey = await web3.PublicKey.createProgramAddress( + [highEntropyBuffer.slice(0, 31)], + base58publicKey, +); console.log(`Generated Program Address: ${programAddressFromKey.toBase58()}`); // Generated Program Address: 3thxPEEz4EDWHNxo1LpEpsAxZryPAHyvNVXJEJWgBgwJ // Find Program address given a PublicKey -let validProgramAddress = await web3.PublicKey.findProgramAddress([Buffer.from('', 'utf8')], programAddressFromKey); +let validProgramAddress = await web3.PublicKey.findProgramAddress( + [Buffer.from("", "utf8")], + programAddressFromKey, +); console.log(`Valid Program Address: ${validProgramAddress}`); // Valid Program Address: C14Gs3oyeXbASzwUpqSymCKpEyccfEuSe8VRar9vJQRE,253 @@ -233,11 +246,11 @@ const web3 = require("@solana/web3.js"); // Airdrop SOL for paying transactions let payer = web3.Keypair.generate(); -let connection = new web3.Connection(web3.clusterApiUrl('devnet'), 'confirmed'); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); let airdropSignature = await connection.requestAirdrop( - payer.publicKey, - web3.LAMPORTS_PER_SOL, + payer.publicKey, + web3.LAMPORTS_PER_SOL, ); await connection.confirmTransaction(airdropSignature); @@ -245,63 +258,74 @@ await connection.confirmTransaction(airdropSignature); // Allocate Account Data let allocatedAccount = web3.Keypair.generate(); let allocateInstruction = web3.SystemProgram.allocate({ - accountPubkey: allocatedAccount.publicKey, - space: 100, -}) + accountPubkey: allocatedAccount.publicKey, + space: 100, +}); let transaction = new web3.Transaction().add(allocateInstruction); -await web3.sendAndConfirmTransaction(connection, transaction, [payer, allocatedAccount]) +await web3.sendAndConfirmTransaction(connection, transaction, [ + payer, + allocatedAccount, +]); // Create Nonce Account let nonceAccount = web3.Keypair.generate(); -let minimumAmountForNonceAccount = await connection.getMinimumBalanceForRentExemption( - web3.NONCE_ACCOUNT_LENGTH, -); +let minimumAmountForNonceAccount = + await connection.getMinimumBalanceForRentExemption(web3.NONCE_ACCOUNT_LENGTH); let createNonceAccountTransaction = new web3.Transaction().add( -web3.SystemProgram.createNonceAccount({ + web3.SystemProgram.createNonceAccount({ fromPubkey: payer.publicKey, noncePubkey: nonceAccount.publicKey, authorizedPubkey: payer.publicKey, lamports: minimumAmountForNonceAccount, -}), + }), ); -await web3.sendAndConfirmTransaction(connection, createNonceAccountTransaction, [payer, nonceAccount]) +await web3.sendAndConfirmTransaction( + connection, + createNonceAccountTransaction, + [payer, nonceAccount], +); // Advance nonce - Used to create transactions as an account custodian let advanceNonceTransaction = new web3.Transaction().add( - web3.SystemProgram.nonceAdvance({ - noncePubkey: nonceAccount.publicKey, - authorizedPubkey: payer.publicKey, - }), + web3.SystemProgram.nonceAdvance({ + noncePubkey: nonceAccount.publicKey, + authorizedPubkey: payer.publicKey, + }), ); -await web3.sendAndConfirmTransaction(connection, advanceNonceTransaction, [payer]) +await web3.sendAndConfirmTransaction(connection, advanceNonceTransaction, [ + payer, +]); // Transfer lamports between accounts let toAccount = web3.Keypair.generate(); let transferTransaction = new web3.Transaction().add( -web3.SystemProgram.transfer({ + web3.SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: toAccount.publicKey, lamports: 1000, -}), + }), ); -await web3.sendAndConfirmTransaction(connection, transferTransaction, [payer]) +await web3.sendAndConfirmTransaction(connection, transferTransaction, [payer]); // Assign a new account to a program let programId = web3.Keypair.generate(); let assignedAccount = web3.Keypair.generate(); let assignTransaction = new web3.Transaction().add( -web3.SystemProgram.assign({ + web3.SystemProgram.assign({ accountPubkey: assignedAccount.publicKey, programId: programId.publicKey, -}), + }), ); -await web3.sendAndConfirmTransaction(connection, assignTransaction, [payer, assignedAccount]); +await web3.sendAndConfirmTransaction(connection, assignTransaction, [ + payer, + assignedAccount, +]); ``` ### Secp256k1Program @@ -313,49 +337,52 @@ The Secp256k1Program is used to verify Secp256k1 signatures, which are used by b #### Example Usage ```javascript -const {keccak_256} = require('js-sha3'); +const { keccak_256 } = require("js-sha3"); const web3 = require("@solana/web3.js"); -const secp256k1 = require('secp256k1'); +const secp256k1 = require("secp256k1"); // Create a Ethereum Address from secp256k1 let secp256k1PrivateKey; do { - secp256k1PrivateKey = web3.Keypair.generate().secretKey.slice(0, 32); + secp256k1PrivateKey = web3.Keypair.generate().secretKey.slice(0, 32); } while (!secp256k1.privateKeyVerify(secp256k1PrivateKey)); -let secp256k1PublicKey = secp256k1.publicKeyCreate(secp256k1PrivateKey, false).slice(1); +let secp256k1PublicKey = secp256k1 + .publicKeyCreate(secp256k1PrivateKey, false) + .slice(1); -let ethAddress = web3.Secp256k1Program.publicKeyToEthAddress(secp256k1PublicKey); -console.log(`Ethereum Address: 0x${ethAddress.toString('hex')}`); +let ethAddress = + web3.Secp256k1Program.publicKeyToEthAddress(secp256k1PublicKey); +console.log(`Ethereum Address: 0x${ethAddress.toString("hex")}`); // Ethereum Address: 0xadbf43eec40694eacf36e34bb5337fba6a2aa8ee // Fund a keypair to create instructions let fromPublicKey = web3.Keypair.generate(); -let connection = new web3.Connection(web3.clusterApiUrl('devnet'), 'confirmed'); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); let airdropSignature = await connection.requestAirdrop( - fromPublicKey.publicKey, - web3.LAMPORTS_PER_SOL, + fromPublicKey.publicKey, + web3.LAMPORTS_PER_SOL, ); await connection.confirmTransaction(airdropSignature); // Sign Message with Ethereum Key -let plaintext = Buffer.from('string address'); +let plaintext = Buffer.from("string address"); let plaintextHash = Buffer.from(keccak_256.update(plaintext).digest()); -let {signature, recid: recoveryId} = secp256k1.ecdsaSign( - plaintextHash, - secp256k1PrivateKey +let { signature, recid: recoveryId } = secp256k1.ecdsaSign( + plaintextHash, + secp256k1PrivateKey, ); // Create transaction to verify the signature let transaction = new Transaction().add( - web3.Secp256k1Program.createInstructionWithEthAddress({ - ethAddress: ethAddress.toString('hex'), - plaintext, - signature, - recoveryId, - }), + web3.Secp256k1Program.createInstructionWithEthAddress({ + ethAddress: ethAddress.toString("hex"), + plaintext, + signature, + recoveryId, + }), ); // Transaction will succeed if the message is verified to be signed by the address @@ -371,61 +398,57 @@ Message is used as another way to construct transactions. You can construct a me #### Example Usage ```javascript -const {Buffer} = require("buffer"); -const bs58 = require('bs58'); -const web3 = require('@solana/web3.js'); +const { Buffer } = require("buffer"); +const bs58 = require("bs58"); +const web3 = require("@solana/web3.js"); let toPublicKey = web3.Keypair.generate().publicKey; let fromPublicKey = web3.Keypair.generate(); -let connection = new web3.Connection( - web3.clusterApiUrl('devnet'), - 'confirmed' -); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); let airdropSignature = await connection.requestAirdrop( - fromPublicKey.publicKey, - web3.LAMPORTS_PER_SOL, + fromPublicKey.publicKey, + web3.LAMPORTS_PER_SOL, ); await connection.confirmTransaction(airdropSignature); let type = web3.SYSTEM_INSTRUCTION_LAYOUTS.Transfer; let data = Buffer.alloc(type.layout.span); -let layoutFields = Object.assign({instruction: type.index}); +let layoutFields = Object.assign({ instruction: type.index }); type.layout.encode(layoutFields, data); let recentBlockhash = await connection.getRecentBlockhash(); let messageParams = { - accountKeys: [ - fromPublicKey.publicKey.toString(), - toPublicKey.toString(), - web3.SystemProgram.programId.toString() - ], - header: { - numReadonlySignedAccounts: 0, - numReadonlyUnsignedAccounts: 1, - numRequiredSignatures: 1, + accountKeys: [ + fromPublicKey.publicKey.toString(), + toPublicKey.toString(), + web3.SystemProgram.programId.toString(), + ], + header: { + numReadonlySignedAccounts: 0, + numReadonlyUnsignedAccounts: 1, + numRequiredSignatures: 1, + }, + instructions: [ + { + accounts: [0, 1], + data: bs58.encode(data), + programIdIndex: 2, }, - instructions: [ - { - accounts: [0, 1], - data: bs58.encode(data), - programIdIndex: 2, - }, - ], - recentBlockhash, + ], + recentBlockhash, }; let message = new web3.Message(messageParams); -let transaction = web3.Transaction.populate( - message, - [fromPublicKey.publicKey.toString()] -); +let transaction = web3.Transaction.populate(message, [ + fromPublicKey.publicKey.toString(), +]); -await web3.sendAndConfirmTransaction(connection, transaction, [fromPublicKey]) +await web3.sendAndConfirmTransaction(connection, transaction, [fromPublicKey]); ``` ### Struct @@ -437,6 +460,7 @@ The struct class is used to create Rust compatible structs in javascript. This c #### Example Usage Struct in Rust: + ```rust pub struct Fee { pub denominator: u64, @@ -445,9 +469,10 @@ pub struct Fee { ``` Using web3: + ```javascript -import BN from 'bn.js'; -import {Struct} from '@solana/web3.js'; +import BN from "bn.js"; +import { Struct } from "@solana/web3.js"; export class Fee extends Struct { denominator: BN; @@ -464,6 +489,7 @@ The Enum class is used to represent a Rust compatible Enum in javascript. The en #### Example Usage Rust: + ```rust pub enum AccountType { Uninitialized, @@ -473,8 +499,9 @@ pub enum AccountType { ``` Web3: + ```javascript -import {Enum} from '@solana/web3.js'; +import { Enum } from "@solana/web3.js"; export class AccountType extends Enum {} ``` @@ -490,13 +517,10 @@ You can create a nonce account by first creating a normal account, then using `S #### Example Usage ```javascript -const web3 = require('@solana/web3.js'); +const web3 = require("@solana/web3.js"); // Create connection -let connection = new web3.Connection( - web3.clusterApiUrl('devnet'), - 'confirmed', -); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); // Generate accounts let account = web3.Keypair.generate(); @@ -504,36 +528,35 @@ let nonceAccount = web3.Keypair.generate(); // Fund account let airdropSignature = await connection.requestAirdrop( - account.publicKey, - web3.LAMPORTS_PER_SOL, + account.publicKey, + web3.LAMPORTS_PER_SOL, ); await connection.confirmTransaction(airdropSignature); // Get Minimum amount for rent exemption let minimumAmount = await connection.getMinimumBalanceForRentExemption( - web3.NONCE_ACCOUNT_LENGTH, + web3.NONCE_ACCOUNT_LENGTH, ); // Form CreateNonceAccount transaction let transaction = new web3.Transaction().add( -web3.SystemProgram.createNonceAccount({ + web3.SystemProgram.createNonceAccount({ fromPubkey: account.publicKey, noncePubkey: nonceAccount.publicKey, authorizedPubkey: account.publicKey, lamports: minimumAmount, -}), + }), ); // Create Nonce Account -await web3.sendAndConfirmTransaction( - connection, - transaction, - [account, nonceAccount] -); +await web3.sendAndConfirmTransaction(connection, transaction, [ + account, + nonceAccount, +]); let nonceAccountData = await connection.getNonce( - nonceAccount.publicKey, - 'confirmed', + nonceAccount.publicKey, + "confirmed", ); console.log(nonceAccountData); @@ -546,12 +569,12 @@ console.log(nonceAccountData); // } let nonceAccountInfo = await connection.getAccountInfo( - nonceAccount.publicKey, - 'confirmed' + nonceAccount.publicKey, + "confirmed", ); let nonceAccountFromInfo = web3.NonceAccount.fromAccountData( - nonceAccountInfo.data + nonceAccountInfo.data, ); console.log(nonceAccountFromInfo); @@ -575,10 +598,12 @@ Vote account is an object that grants the capability of decoding vote accounts f #### Example Usage ```javascript -const web3 = require('@solana/web3.js'); +const web3 = require("@solana/web3.js"); let voteAccountInfo = await connection.getProgramAccounts(web3.VOTE_PROGRAM_ID); -let voteAccountFromData = web3.VoteAccount.fromAccountData(voteAccountInfo[0].account.data); +let voteAccountFromData = web3.VoteAccount.fromAccountData( + voteAccountInfo[0].account.data, +); console.log(voteAccountFromData); /* VoteAccount { @@ -650,11 +675,11 @@ const web3 = require("@solana/web3.js"); // Fund a key to create transactions let fromPublicKey = web3.Keypair.generate(); -let connection = new web3.Connection(web3.clusterApiUrl('devnet'), 'confirmed'); +let connection = new web3.Connection(web3.clusterApiUrl("devnet"), "confirmed"); let airdropSignature = await connection.requestAirdrop( - fromPublicKey.publicKey, - web3.LAMPORTS_PER_SOL, + fromPublicKey.publicKey, + web3.LAMPORTS_PER_SOL, ); await connection.confirmTransaction(airdropSignature); @@ -663,20 +688,29 @@ let stakeAccount = web3.Keypair.generate(); let authorizedAccount = web3.Keypair.generate(); /* Note: This is the minimum amount for a stake account -- Add additional Lamports for staking For example, we add 50 lamports as part of the stake */ -let lamportsForStakeAccount = (await connection.getMinimumBalanceForRentExemption(web3.StakeProgram.space)) + 50; +let lamportsForStakeAccount = + (await connection.getMinimumBalanceForRentExemption( + web3.StakeProgram.space, + )) + 50; let createAccountTransaction = web3.StakeProgram.createAccount({ - fromPubkey: fromPublicKey.publicKey, - authorized: new web3.Authorized(authorizedAccount.publicKey, authorizedAccount.publicKey), - lamports: lamportsForStakeAccount, - lockup: new web3.Lockup(0, 0, fromPublicKey.publicKey), - stakePubkey: stakeAccount.publicKey + fromPubkey: fromPublicKey.publicKey, + authorized: new web3.Authorized( + authorizedAccount.publicKey, + authorizedAccount.publicKey, + ), + lamports: lamportsForStakeAccount, + lockup: new web3.Lockup(0, 0, fromPublicKey.publicKey), + stakePubkey: stakeAccount.publicKey, }); -await web3.sendAndConfirmTransaction(connection, createAccountTransaction, [fromPublicKey, stakeAccount]); +await web3.sendAndConfirmTransaction(connection, createAccountTransaction, [ + fromPublicKey, + stakeAccount, +]); // Check that stake is available let stakeBalance = await connection.getBalance(stakeAccount.publicKey); -console.log(`Stake balance: ${stakeBalance}`) +console.log(`Stake balance: ${stakeBalance}`); // Stake balance: 2282930 // We can verify the state of our stake. This may take some time to become active @@ -686,35 +720,42 @@ console.log(`Stake state: ${stakeState.state}`); // To delegate our stake, we get the current vote accounts and choose the first let voteAccounts = await connection.getVoteAccounts(); -let voteAccount = voteAccounts.current.concat( - voteAccounts.delinquent, -)[0]; +let voteAccount = voteAccounts.current.concat(voteAccounts.delinquent)[0]; let votePubkey = new web3.PublicKey(voteAccount.votePubkey); // We can then delegate our stake to the voteAccount let delegateTransaction = web3.StakeProgram.delegate({ - stakePubkey: stakeAccount.publicKey, - authorizedPubkey: authorizedAccount.publicKey, - votePubkey: votePubkey, + stakePubkey: stakeAccount.publicKey, + authorizedPubkey: authorizedAccount.publicKey, + votePubkey: votePubkey, }); -await web3.sendAndConfirmTransaction(connection, delegateTransaction, [fromPublicKey, authorizedAccount]); +await web3.sendAndConfirmTransaction(connection, delegateTransaction, [ + fromPublicKey, + authorizedAccount, +]); // To withdraw our funds, we first have to deactivate the stake let deactivateTransaction = web3.StakeProgram.deactivate({ - stakePubkey: stakeAccount.publicKey, - authorizedPubkey: authorizedAccount.publicKey, + stakePubkey: stakeAccount.publicKey, + authorizedPubkey: authorizedAccount.publicKey, }); -await web3.sendAndConfirmTransaction(connection, deactivateTransaction, [fromPublicKey, authorizedAccount]); +await web3.sendAndConfirmTransaction(connection, deactivateTransaction, [ + fromPublicKey, + authorizedAccount, +]); // Once deactivated, we can withdraw our funds let withdrawTransaction = web3.StakeProgram.withdraw({ - stakePubkey: stakeAccount.publicKey, - authorizedPubkey: authorizedAccount.publicKey, - toPubkey: fromPublicKey.publicKey, - lamports: stakeBalance, + stakePubkey: stakeAccount.publicKey, + authorizedPubkey: authorizedAccount.publicKey, + toPubkey: fromPublicKey.publicKey, + lamports: stakeBalance, }); -await web3.sendAndConfirmTransaction(connection, withdrawTransaction, [fromPublicKey, authorizedAccount]); +await web3.sendAndConfirmTransaction(connection, withdrawTransaction, [ + fromPublicKey, + authorizedAccount, +]); ``` ### Authorized @@ -734,7 +775,12 @@ Lockup is used in conjunction with the [StakeProgram](javascript-api.md#StakePro #### Example Usage ```javascript -const {Authorized, Keypair, Lockup, StakeProgram} = require("@solana/web3.js"); +const { + Authorized, + Keypair, + Lockup, + StakeProgram, +} = require("@solana/web3.js"); let account = Keypair.generate(); let stakeAccount = Keypair.generate(); @@ -742,13 +788,14 @@ let authorized = new Authorized(account.publicKey, account.publicKey); let lockup = new Lockup(0, 0, account.publicKey); let createStakeAccountInstruction = StakeProgram.createAccount({ - fromPubkey: account.publicKey, - authorized: authorized, - lamports: 1000, - lockup: lockup, - stakePubkey: stakeAccount.publicKey + fromPubkey: account.publicKey, + authorized: authorized, + lamports: 1000, + lockup: lockup, + stakePubkey: stakeAccount.publicKey, }); ``` + The above code creates a `createStakeAccountInstruction` to be used when creating an account with the `StakeProgram`. The Lockup is set to 0 for both the epoch and Unix timestamp, disabling lockup for the account. See [StakeProgram](javascript-api.md#StakeProgram) for more. diff --git a/docs/src/developing/on-chain-programs/developing-c.md b/docs/src/developing/on-chain-programs/developing-c.md index df1d551f13..52469efcd7 100644 --- a/docs/src/developing/on-chain-programs/developing-c.md +++ b/docs/src/developing/on-chain-programs/developing-c.md @@ -33,8 +33,7 @@ for an example of a C program. First setup the environment: - Install the latest Rust stable from https://rustup.rs -- Install the latest Solana command-line tools from - https://docs.solana.com/cli/install-solana-cli-tools +- Install the latest [Solana command-line tools](../../cli/install-solana-cli-tools.md) Then build using make: diff --git a/docs/src/developing/on-chain-programs/developing-rust.md b/docs/src/developing/on-chain-programs/developing-rust.md index ee948933cc..b6406902b3 100644 --- a/docs/src/developing/on-chain-programs/developing-rust.md +++ b/docs/src/developing/on-chain-programs/developing-rust.md @@ -59,8 +59,7 @@ For example: First setup the environment: - Install the latest Rust stable from https://rustup.rs/ -- Install the latest Solana command-line tools from - https://docs.solana.com/cli/install-solana-cli-tools +- Install the latest [Solana command-line tools](../../cli/install-solana-cli-tools.md) The normal cargo build is available for building programs against your host machine which can be used for unit testing: diff --git a/docs/src/developing/programming-model/transactions.md b/docs/src/developing/programming-model/transactions.md index c93dd80de6..42e6ca2d23 100644 --- a/docs/src/developing/programming-model/transactions.md +++ b/docs/src/developing/programming-model/transactions.md @@ -202,7 +202,7 @@ found in [Accounts](accounts.md#signers) ## Recent Blockhash -A transaction includes a recent [blockhash](terminology.md#blockhash) to prevent +A transaction includes a recent [blockhash](../../terminology.md#blockhash) to prevent duplication and to give transactions lifetimes. Any transaction that is completely identical to a previous one is rejected, so adding a newer blockhash allows multiple transactions to repeat the exact same action. Transactions also diff --git a/docs/src/integrations/retrying-transactions.md b/docs/src/integrations/retrying-transactions.md index c8152ab1a0..f3bf9f4f77 100644 --- a/docs/src/integrations/retrying-transactions.md +++ b/docs/src/integrations/retrying-transactions.md @@ -7,7 +7,7 @@ title: Retrying Transactions On some occasions, a seemingly valid transaction may be dropped before it is included in a block. This most often occurs during periods of network congestion, when an RPC node fails to rebroadcast the transaction to the -[leader](https://docs.solana.com/terminology#leader). To an end-user, it may +[leader](../terminology#leader). To an end-user, it may appear as if their transaction disappears entirely. While RPC nodes are equipped with a generic rebroadcasting algorithm, application developers are also capable of developing their own custom rebroadcasting logic. @@ -25,6 +25,7 @@ Fact Sheet are submitted - Before re-signing any transaction, it is **very important** to ensure that the initial transaction’s blockhash has expired + ::: ## The Journey of a Transaction @@ -37,7 +38,7 @@ so that they can be processed into a block. There are two main ways in which a transaction can be sent to leaders: 1. By proxy via an RPC server and the - [sendTransaction](https://docs.solana.com/developing/clients/jsonrpc-api#sendtransaction) + [sendTransaction](../developing/clients/jsonrpc-api#sendtransaction) JSON-RPC method 2. Directly to leaders via a [TPU Client](https://docs.rs/solana-client/1.7.3/solana_client/tpu_client/index.html) @@ -50,9 +51,9 @@ outside of what the client and the relaying RPC nodes are aware of. In the case of a TPU client, rebroadcast and leader forwarding is handled entirely by the client software. -![Transaction Journey](/img/rt-tx-journey.png) +![Transaction Journey](../../static/img/rt-tx-journey.png) - + ### How RPC Nodes Broadcast Transactions @@ -64,7 +65,7 @@ communicate with one another, but does not provide any guarantees regarding transaction delivery. Because Solana’s leader schedule is known in advance of every -[epoch](https://docs.solana.com/terminology#epoch) (~2 days), an RPC node will +[epoch](../terminology#epoch) (~2 days), an RPC node will broadcast its transaction directly to the current and next leaders. This is in contrast to other gossip protocols such as Ethereum that propagate transactions randomly and broadly across the entire network. By default, RPC nodes will try @@ -89,7 +90,7 @@ The TPU processes transactions in five distinct phases: - [Proof of History Service](https://github.com/solana-labs/solana/blob/cd6f931223181d5a1d47cba64e857785a175a760/poh/src/poh_service.rs) - [Broadcast Stage](https://github.com/solana-labs/solana/blob/cd6f931223181d5a1d47cba64e857785a175a760/core/src/tpu.rs#L136) -![TPU Overview](/img/rt-tpu-jito-labs.png) +![TPU Overview](../../static/img/rt-tpu-jito-labs.png) Of these five phases, the Fetch Stage is responsible for receiving transactions. Within the Fetch Stage, validators will categorize incoming transactions @@ -132,15 +133,15 @@ it is processed. The first scenario involves transactions that are submitted via an RPC pool. Occasionally, part of the RPC pool can be sufficiently ahead of the rest of the pool. This can cause issues when nodes within the pool are required to work together. In this example, the transaction’s -[recentBlockhash](https://docs.solana.com/developing/programming-model/transactions#recent-blockhash) +[recentBlockhash](../developing/programming-model/transactions#recent-blockhash) is queried from the advanced part of the pool (Backend A). When the transaction is submitted to the lagging part of the pool (Backend B), the nodes will not recognize the advanced blockhash and will drop the transaction. This can be detected upon transaction submission if developers enable -[preflight checks](https://docs.solana.com/developing/clients/jsonrpc-api#sendtransaction) +[preflight checks](../developing/clients/jsonrpc-api#sendtransaction) on `sendTransaction`. -![Dropped via RPC Pool](/img/rt-dropped-via-rpc-pool.png) +![Dropped via RPC Pool](../../static/img/rt-dropped-via-rpc-pool.png) Temporarily network forks can also result in dropped transactions. If a validator is slow to replay its blocks within the Banking Stage, it may end up @@ -150,7 +151,7 @@ minority fork. After the transaction is submitted, the cluster can then switch away from its minority fork before the transaction is processed. In this scenario, the transaction is dropped due to the blockhash not being found. -![Dropped due to Minority Fork (Before Processed)](/img/rt-dropped-minority-fork-pre-process.png) +![Dropped due to Minority Fork (Before Processed)](../../static/img/rt-dropped-minority-fork-pre-process.png) ### After a transaction is processed and before it is finalized @@ -162,7 +163,7 @@ would fail to reach consensus with the majority of validators that do not recognize the minority fork. At this time, the transaction would be dropped before it could be finalized. -![Dropped due to Minority Fork (After Processed)](/img/rt-dropped-minority-fork-post-process.png) +![Dropped due to Minority Fork (After Processed)](../../static/img/rt-dropped-minority-fork-post-process.png) ## Handling Dropped Transactions @@ -189,7 +190,7 @@ the transaction will be processed or finalized by the cluster. - `skipPreflight`: `boolean` - if true, skip the preflight transaction checks (default: false) - (optional) `preflightCommitment`: `string` - - [Commitment](https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment) + [Commitment](../developing/clients/jsonrpc-api#configuring-state-commitment) level to use for preflight simulations against the bank slot (default: "finalized"). - (optional) `encoding`: `string` - Encoding used for the transaction data. @@ -203,8 +204,9 @@ Response - `transaction id`: `string` - First transaction signature embedded in the transaction, as base-58 encoded string. This transaction id can be used with - [getSignatureStatuses](https://docs.solana.com/developing/clients/jsonrpc-api#getsignaturestatuses) + [getSignatureStatuses](../developing/clients/jsonrpc-api#getsignaturestatuses) to poll for status updates. + ::: ## Customizing Rebroadcast Logic @@ -217,9 +219,9 @@ developers to manually control the retry process A common pattern for manually retrying transactions involves temporarily storing the `lastValidBlockHeight` that comes from -[getLatestBlockhash](https://docs.solana.com/developing/clients/jsonrpc-api#getlatestblockhash). +[getLatestBlockhash](../developing/clients/jsonrpc-api#getlatestblockhash). Once stashed, an application can then -[poll the cluster’s blockheight](https://docs.solana.com/developing/clients/jsonrpc-api#getblockheight) +[poll the cluster’s blockheight](../developing/clients/jsonrpc-api#getblockheight) and manually retry the transaction at an appropriate interval. In times of network congestion, it’s advantageous to set `maxRetries` to 0 and manually rebroadcast via a custom algorithm. While some applications may employ an @@ -287,7 +289,7 @@ const sleep = async (ms: number) => { When polling via `getLatestBlockhash`, applications should specify their intended -[commitment](https://docs.solana.com/developing/clients/jsonrpc-api#configuring-state-commitment) +[commitment](../developing/clients/jsonrpc-api#configuring-state-commitment) level. By setting its commitment to `confirmed` (voted on) or `finalized` (~30 blocks after `confirmed`), an application can avoid polling a blockhash from a minority fork. @@ -329,6 +331,6 @@ In Solana, a dropped transaction can be safely discarded once the blockhash it references is older than the `lastValidBlockHeight` received from `getLatestBlockhash`. Developers should keep track of this `lastValidBlockHeight` by querying -[`getEpochInfo`](https://docs.solana.com/developing/clients/jsonrpc-api#getepochinfo) +[`getEpochInfo`](../developing/clients/jsonrpc-api#getepochinfo) and comparing with `blockHeight` in the response. Once a blockhash is invalidated, clients may re-sign with a newly-queried blockhash.