From 498692a62a845f557554effaf3aacc1a3e92f8b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20=C4=86wirko?= Date: Sat, 29 Jun 2024 16:04:54 +0200 Subject: [PATCH] add smart contract interactions --- README.md | 7 ++ deploy-smart-contract.js | 6 +- package-lock.json | 16 ++-- package.json | 8 +- smart-contract-interactions.js | 146 +++++++++++++++++++++++++++++++++ 5 files changed, 168 insertions(+), 15 deletions(-) create mode 100644 smart-contract-interactions.js diff --git a/README.md b/README.md index 7601eab..72d7a1e 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,13 @@ git checkout - **Article**: [Mastering smart contract deployment with MultiversX JavaScript SDK](https://www.julian.io/articles/multiversx-js-sdk-sc-deployment.html) - **Video**: [Mastering smart contract deployment with MultiversX JavaScript SDK](https://www.youtube.com/watch?v=Rk-vHqd2avs) +### Example 4: Smart contract interactions + +- **Tag**: `smart-contract-interactions` +- **Description**: This example demonstrates how to interact with a smart contract using the MultiversX JavaScript SDK on the devnet. +- **Article**: [Step-by-step guide to MultiversX smart contract interactions with JavaScript SDK](https://www.julian.io/articles/multiversx-js-sdk-sc-interactions.html) +- **Video**: [Step-by-step guide to MultiversX smart contract interactions with JavaScript SDK](https://www.youtube.com/watch?v=TMDC5yxT4_c) + ## Security and Wallet Information The examples use a demo wallet with a hardcoded password. All interactions occur on the **devnet** (development network of MultiversX), ensuring that it is safe to expose the wallet credentials. The devnet is designed for testing and development, involving no real assets. Don't do this on the mainnet. diff --git a/deploy-smart-contract.js b/deploy-smart-contract.js index fcbb81f..e2b0c68 100644 --- a/deploy-smart-contract.js +++ b/deploy-smart-contract.js @@ -5,6 +5,7 @@ import { SmartContractTransactionsFactory, Code, Address, + AbiRegistry, TransactionWatcher, SmartContractTransactionsOutcomeParser, TransactionsConverter, @@ -29,12 +30,13 @@ const deploySmartContract = async () => { // Load ABI file (not required for now, but will be useful when interacting with the SC) // Although it would be helpful if we had initial arguments to pass const abiFile = await promises.readFile("./piggybank.abi.json", "UTF-8"); + const abiObj = JSON.parse(abiFile); // Prepare transfer transactions factory const factoryConfig = new TransactionsFactoryConfig({ chainID: "D" }); let scFactory = new SmartContractTransactionsFactory({ config: factoryConfig, - abi: abiFile, + abi: AbiRegistry.create(abiObj), }); // Prepare deploy transaction @@ -82,8 +84,6 @@ const deploySmartContract = async () => { converter.transactionOnNetworkToOutcome(transactionOnNetwork); const parsedOutcome = parser.parseDeploy({ transactionOutcome }); - console.log(parsedOutcome); - console.log( `Smart Contract deployed. Here it is:\nhttps://devnet-explorer.multiversx.com/accounts/${parsedOutcome.contracts[0].address}\n\nCheck the transaction in the Explorer:\nhttps://devnet-explorer.multiversx.com/transactions/${txHash}` ); diff --git a/package-lock.json b/package-lock.json index 29fff40..21fa4dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,9 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@multiversx/sdk-core": "^13.1.0", + "@multiversx/sdk-core": "^13.2.1", "@multiversx/sdk-network-providers": "^2.4.3", - "@multiversx/sdk-wallet": "^4.4.0" + "@multiversx/sdk-wallet": "^4.5.0" } }, "node_modules/@multiversx/sdk-bls-wasm": { @@ -23,9 +23,9 @@ } }, "node_modules/@multiversx/sdk-core": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-core/-/sdk-core-13.1.0.tgz", - "integrity": "sha512-Z8lDPX4mKKD0QUtAqRzM0bf8zex3xCWRd5yYRLCxjW1F+jYg+9LjIQjkyvDSWGYJHY4IJeYqkORoS3gQG8NDbA==", + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-core/-/sdk-core-13.2.1.tgz", + "integrity": "sha512-qnC4Q2x+lc7jw5GYja5yvRhqKaJ+2m95txNS6FwLXPb/MxW0ZwRZsAt4qByxOF5hpx6m9v3bJnKFFZn9VrHcNw==", "dependencies": { "@multiversx/sdk-transaction-decoder": "1.0.2", "bech32": "1.1.4", @@ -73,9 +73,9 @@ "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" }, "node_modules/@multiversx/sdk-wallet": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-wallet/-/sdk-wallet-4.4.0.tgz", - "integrity": "sha512-wS4P8a2ts3cNaSLUw9VFf4yhWSMTYng+nyHKi3/9QalLP5lxBumUfD/mUkb9sK13UPJ5Xp/zB3j8a4Qdllw2Ag==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-wallet/-/sdk-wallet-4.5.0.tgz", + "integrity": "sha512-2E/bMDcbFV7zl0KuSEPdNrIjIaMOp4e1BfctPO1q2F0Hd6aAHYO7bZIXNcYEj2v1zgvESybgVh5Zi5vQYtNZRg==", "dependencies": { "@multiversx/sdk-bls-wasm": "0.3.5", "@noble/ed25519": "1.7.3", diff --git a/package.json b/package.json index a50668a..9f18e72 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,10 @@ "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", - "license": "ISC", + "license": "MIT", "dependencies": { - "@multiversx/sdk-core": "^13.1.0", + "@multiversx/sdk-core": "^13.2.1", "@multiversx/sdk-network-providers": "^2.4.3", - "@multiversx/sdk-wallet": "^4.4.0" + "@multiversx/sdk-wallet": "^4.5.0" } -} +} \ No newline at end of file diff --git a/smart-contract-interactions.js b/smart-contract-interactions.js new file mode 100644 index 0000000..5148045 --- /dev/null +++ b/smart-contract-interactions.js @@ -0,0 +1,146 @@ +import { promises } from "node:fs"; +import { + Address, + SmartContractTransactionsFactory, + TransactionsFactoryConfig, + TransactionComputer, + AbiRegistry, + QueryRunnerAdapter, + SmartContractQueriesController, +} from "@multiversx/sdk-core"; +import { + syncAndGetAccount, + senderAddress, + getSigner, + apiNetworkProvider, +} from "./setup.js"; + +/** + * Replace with your own deployed piggy bank smart contract + * check deploy-smart-contract.js on how to deploy one + */ +const PIGGYBANK_CONTRACT_ADDRESS = + "erd1qqqqqqqqqqqqqpgqtrajzw4vq0zxccdt9u66cvgg6vz8c6cwnegqkfqkpq"; + +/** + * Load ABI file + */ +const getAbi = async () => { + const abiFile = await promises.readFile("./piggybank.abi.json", "UTF-8"); + return JSON.parse(abiFile); +}; + +const scTransaction = async ({ functionName, args, amount }) => { + const user = await syncAndGetAccount(); + const computer = new TransactionComputer(); + const signer = await getSigner(); + + const abiObj = await getAbi(); + + const factoryConfig = new TransactionsFactoryConfig({ chainID: "D" }); + const factory = new SmartContractTransactionsFactory({ + config: factoryConfig, + abi: AbiRegistry.create(abiObj), + }); + + const transaction = factory.createTransactionForExecute({ + sender: new Address(senderAddress), + contract: Address.fromBech32(PIGGYBANK_CONTRACT_ADDRESS), + function: functionName, + gasLimit: 5000000, + arguments: args || [], + nativeTransferAmount: amount, + }); + + // Increase the nonce + transaction.nonce = user.getNonceThenIncrement(); + + // Serialize the transaction for signing + const serializedTransaction = computer.computeBytesForSigning(transaction); + + // Sign the transaction with our signer + transaction.signature = await signer.sign(serializedTransaction); + + // Broadcast the transaction + const txHash = await apiNetworkProvider.sendTransaction(transaction); + + console.log( + "Check in the Explorer: ", + `https://devnet-explorer.multiversx.com/transactions/${txHash}` + ); +}; + +/** + * Call the createPiggy endpoint on the PiggyBank smart contract + * https://github.com/xdevguild/piggy-bank-sc/blob/master/src/lib.rs#L25 + * We pass the unix timestamp in the future + */ +const createPiggy = async () => { + await scTransaction({ + functionName: "createPiggy", + args: [1750686756], + }); +}; + +/** + * Call the addAmount endpoint on the PiggyBank smart contract + * https://github.com/xdevguild/piggy-bank-sc/blob/master/src/lib.rs#L42 + */ +const addAmount = async () => { + await scTransaction({ + functionName: "addAmount", + amount: 1000000000000000n, + }); +}; + +/** + * Query the getLockedAmount endpoint on the PiggyBank smart contract + * https://github.com/xdevguild/piggy-bank-sc/blob/master/src/lib.rs#L92 + */ +const getLockedAmount = async () => { + const abiObj = await getAbi(); + + const queryRunner = new QueryRunnerAdapter({ + networkProvider: apiNetworkProvider, + }); + + const controller = new SmartContractQueriesController({ + queryRunner: queryRunner, + abi: AbiRegistry.create(abiObj), + }); + + const query = controller.createQuery({ + contract: PIGGYBANK_CONTRACT_ADDRESS, + function: "getLockedAmount", + arguments: [senderAddress], + }); + + const response = await controller.runQuery(query); + + const [amount] = controller.parseQueryResponse(response); + + // The returned amount is a BigNumber + console.log("Locked amount is: ", amount.valueOf()); +}; + +/** + * Here we will manage which function to call + */ +const smartContractInteractions = () => { + const args = process.argv.slice(2); + const functionName = args[0]; + + const functions = { + createPiggy, + addAmount, + getLockedAmount, + }; + + if (functionName in functions) { + functions[functionName](); + } else { + console.log("Function not found!"); + } +}; + +smartContractInteractions();