diff --git a/docs/smart-contracts/guides/conventions/install-deploy-contract-with-code.mdx b/docs/smart-contracts/guides/conventions/install-deploy-contract-with-code.mdx new file mode 100644 index 000000000..4773011d4 --- /dev/null +++ b/docs/smart-contracts/guides/conventions/install-deploy-contract-with-code.mdx @@ -0,0 +1,249 @@ +--- +title: Install and deploy a smart contract with code +hide_table_of_contents: true +draft: true +--- + + + Install and deploy a smart contract with code + + + + + +This guide will walk you through the process of installing and deploying a smart contract using [js-stellar-sdk](https://github.com/stellar/js-stellar-sdk). We will cover the setup of a sample Rust contract, creating a Node.js project to manage the deployment, and finally, installing the wasm of the contract and deploying it to the network + +# Prerequisites + +Before you begin, ensure you have the following installed: + +1. [Rust](https://www.rust-lang.org/) and Cargo (for compiling smart contracts) +2. [Node.js](https://nodejs.org/en) and npm (for running JavaScript deployment scripts) +3. [Soroban CLI](../../getting-started/setup.mdx#install-the-soroban-cli) + +# Initialize a sample Rust Contract + +```bash +soroban contract init hello-world +cd hello-world +soroban contract build +``` + +This sequence of commands creates a new directory for your project, initializes a new Soroban smart contract within that directory, and builds the contract. The build generates .wasm file in the path `hello-word/target/wasm32-unknown-unknown/release/hello_world.wasm` + +# Create a Node Project + +1. Create a new directory for your Node.js project and navigate into it: + +```bash +mkdir deploy-contract +cd deploy-contract +``` + +2. Initialize a new Node.js project and install necessary dependencies: + +```bash +npm init -y +npm install @stellar/stellar-sdk fs +``` + +# Set Up Deployment Scripts + +## Imports + +Import necessary modules in your JavaScript file: + +```javascript +import * as StellarSDK from "@stellar/stellar-sdk"; +import fs from "fs"; +``` + +## Installing the WASM on the network + +Create a function to upload the compiled WASM file: + +```javascript +async function uploadWasm(filePath) { + const bytecode = fs.readFileSync(filePath); + const account = await server.getAccount(sourceKeypair.publicKey()); + const operation = StellarSDK.Operation.uploadContractWasm({ wasm: bytecode }); + return await buildAndSendTransaction(account, operation); +} +``` + +This function reads the compiled WASM file, retrieves account details from the network, and installs the bytecode using `uploadContractWasm` Stellar operation, which when wrapped in a transaction is sent to the network. + +## Deploy contract + +Deploy the contract by referencing the WASM hash: + +```javascript +async function deployContract(response) { + const account = await server.getAccount(sourceKeypair.publicKey()); + const operation = StellarSDK.Operation.createCustomContract({ + wasmHash: response.returnValue.bytes(), + address: StellarSDK.Address.fromString(sourceKeypair.publicKey()), + salt: response.hash, + }); + const responseDeploy = await buildAndSendTransaction(account, operation); + const contractAddress = StellarSDK.StrKey.encodeContract( + StellarSDK.Address.fromScAddress( + responseDeploy.returnValue.address(), + ).toBuffer(), + ); + console.log(contractAddress); +} +``` + +This function uses the WASM hash to deploy the contract using the `createCustomContract` stellar operation, which when wrapped in a transaction is sent to the network, generating a contract address. + +## Building, Signing and Sending the Transaction + +Handle the building, signing, and sending of transactions: + +```javascript +async function buildAndSendTransaction(account, operations) { + const transaction = new StellarSDK.TransactionBuilder(account, { + fee: StellarSDK.BASE_FEE, + networkPassphrase: StellarSDK.Networks.TESTNET, + }) + .addOperation(operations) + .setTimeout(30) + .build(); + + const tx = await server.prepareTransaction(transaction); + tx.sign(sourceKeypair); + + console.log("Submitting transaction..."); + let response = await server.sendTransaction(tx); + const hash = response.hash; + console.log(`Transaction hash: ${hash}`); + console.log("Awaiting confirmation..."); + + while (true) { + response = await server.getTransaction(hash); + if (response.status !== "NOT_FOUND") { + break; + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + + if (response.status === "SUCCESS") { + console.log("Transaction successful."); + return response; + } else { + console.log("Transaction failed."); + throw new Error("Transaction failed"); + } +} +``` + +This function constructs a transaction, signs it, and submits it to the network, handling any necessary retries for transaction confirmation. + +# Running the Script + +Execute the deployment script: + +```javascript +const server = new StellarSDK.SorobanRpc.Server( + "https://soroban-testnet.stellar.org:443", +); +const sourceKeypair = StellarSDK.Keypair.fromSecret("Your_Secret_Key"); +const wasmFilePath = + "../hello-world/target/wasm32-unknown-unknown/release/hello_world.wasm"; // Adjust this path as necessary + +try { + let uploadResponse = await uploadWasm(wasmFilePath); + await deployContract(uploadResponse); +} catch (error) { + console.error(error); +} +``` + +Replace "Your_Secret_Key" with your actual secret key. This script initiates the upload of the WASM file and deploys the contract, resulting in a contract address where the contract is deployed. + +This is just demo code, so ensure that you handle secrets and private keys securely in production environments and never expose them in your code repositories. This guide should provide you with a clear path to installing and deploying your smart contracts using Javascript code. + +# Complete Script + +Here are all the snippets stacked together in a single file for convenience: + +```javascript +import * as StellarSDK from "@stellar/stellar-sdk"; +import fs from "fs"; +async function uploadWasm(filePath) { + const bytecode = fs.readFileSync(filePath); + const account = await server.getAccount(sourceKeypair.publicKey()); + const operation = StellarSDK.Operation.uploadContractWasm({ wasm: bytecode }); + return await buildAndSendTransaction(account, operation); +} +async function deployContract(response) { + const account = await server.getAccount(sourceKeypair.publicKey()); + const operation = StellarSDK.Operation.createCustomContract({ + wasmHash: response.returnValue.bytes(), + address: StellarSDK.Address.fromString(sourceKeypair.publicKey()), + salt: response.hash, + }); + const responseDeploy = await buildAndSendTransaction(account, operation); + const contractAddress = StellarSDK.StrKey.encodeContract( + StellarSDK.Address.fromScAddress( + responseDeploy.returnValue.address(), + ).toBuffer(), + ); + console.log(contractAddress); +} +async function buildAndSendTransaction(account, operations) { + const transaction = new StellarSDK.TransactionBuilder(account, { + fee: StellarSDK.BASE_FEE, + networkPassphrase: StellarSDK.Networks.TESTNET, + }) + .addOperation(operations) + .setTimeout(30) + .build(); + + const tx = await server.prepareTransaction(transaction); + tx.sign(sourceKeypair); + + console.log("Submitting transaction..."); + let response = await server.sendTransaction(tx); + const hash = response.hash; + console.log(`Transaction hash: ${hash}`); + console.log("Awaiting confirmation..."); + + while (true) { + response = await server.getTransaction(hash); + if (response.status !== "NOT_FOUND") { + break; + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + + if (response.status === "SUCCESS") { + console.log("Transaction successful."); + return response; + } else { + console.log("Transaction failed."); + throw new Error("Transaction failed"); + } +} + +const server = new StellarSDK.SorobanRpc.Server( + "https://soroban-testnet.stellar.org:443", +); +const sourceKeypair = StellarSDK.Keypair.fromSecret("Your_Secret_Key"); +const wasmFilePath = + "../hello-world/target/wasm32-unknown-unknown/release/hello_world.wasm"; // Adjust this path as necessary + +try { + let uploadResponse = await uploadWasm(wasmFilePath); + await deployContract(uploadResponse); +} catch (error) { + console.error(error); +} +```