From 3930d706de2c1a213c24c03eace26bf162c6d179 Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 16 Apr 2025 15:58:28 +0300 Subject: [PATCH 01/15] Move cookbook from examples --- cookbook/account.js | 137 + cookbook/accountManagement.js | 258 ++ cookbook/addresses.js | 85 + cookbook/cookbook.md | 3168 ++++++++++++++++++++++ cookbook/delegation.js | 383 +++ cookbook/entrypoints.js | 34 + cookbook/generate.py | 129 + cookbook/guarded.js | 130 + cookbook/networkProviders.js | 447 ++++ cookbook/package-lock.json | 4620 +++++++++++++++++++++++++++++++++ cookbook/package.json | 9 + cookbook/relayed.js | 149 ++ cookbook/signingObjects.js | 118 + cookbook/smartContracts.js | 533 ++++ cookbook/tokens.js | 417 +++ cookbook/transactions.js | 205 ++ cookbook/verifySIgnatures.js | 147 ++ cookbook/wallets.js | 141 + 18 files changed, 11110 insertions(+) create mode 100644 cookbook/account.js create mode 100644 cookbook/accountManagement.js create mode 100644 cookbook/addresses.js create mode 100644 cookbook/cookbook.md create mode 100644 cookbook/delegation.js create mode 100644 cookbook/entrypoints.js create mode 100644 cookbook/generate.py create mode 100644 cookbook/guarded.js create mode 100644 cookbook/networkProviders.js create mode 100644 cookbook/package-lock.json create mode 100644 cookbook/package.json create mode 100644 cookbook/relayed.js create mode 100644 cookbook/signingObjects.js create mode 100644 cookbook/smartContracts.js create mode 100644 cookbook/tokens.js create mode 100644 cookbook/transactions.js create mode 100644 cookbook/verifySIgnatures.js create mode 100644 cookbook/wallets.js diff --git a/cookbook/account.js b/cookbook/account.js new file mode 100644 index 00000000..925c01f6 --- /dev/null +++ b/cookbook/account.js @@ -0,0 +1,137 @@ +import { Account, DevnetEntrypoint, KeyPair, Mnemonic, UserSecretKey } from '@multiversx/sdk-core'; // md-ignore +import path from 'path'; // md-ignore + +// ## Creating Accounts + +// You can initialize an account directly from the entrypoint. Keep in mind that the account is network agnostic, meaning it doesn't matter which entrypoint is used. +// Accounts are used for signing transactions and messages and managing the account's nonce. They can also be saved to a PEM or keystore file for future use. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const account = entrypoint.createAccount(); +} +// ``` + +// ### Other Ways to Instantiate an Account + +// #### From a Secret Key +// ```js +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); + + const accountFromSecretKey = new Account(secretKey); +} +// ``` + +// #### From a PEM file +// ```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const accountFromPem = Account.newFromPem(filePath); +} +// ``` + +// #### From a Keystore File +// ```js +{ + const keystorePath = path.join("src", "testdata", "testwallets", "alice.json"); + const accountFromKeystore = Account.newFromKeystore({ + filePath: keystorePath, + password: "password" + }); +} +// ``` + +// #### From a Mnemonic +// ```js + +const mnemonic = Mnemonic.generate(); +const accountFromMnemonic = Account.newFromMnemonic(mnemonic.getText()); +// ``` + +// #### From a KeyPair + +// ```js +const keypair = KeyPair.generate(); +const accountFromKeyPairs = Account.newFromKeypair(keypair); +// ``` + +// ### Managing the Account Nonce + +// An account has a `nonce` property that the user is responsible for managing. +// You can fetch the nonce from the network and increment it after each transaction. +// Each transaction must have the correct nonce, otherwise it will fail to execute. + +// ```js +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const key = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); + + const accountWithNonce = new Account(key); + const entrypoint = new DevnetEntrypoint(); + + // Fetch the current nonce from the network // md-as-comment + accountWithNonce.nonce = await entrypoint.recallAccountNonce(accountWithNonce.address); + + // Create and send a transaction here... + + // Increment nonce after each transaction // md-as-comment + const nonce = accountWithNonce.getNonceThenIncrement(); +} +// ``` + +// For more details, see the [Creating Transactions](#creating-transactions) section. + +// #### Saving the Account to a File + +// Accounts can be saved to either a PEM file or a keystore file. +// While PEM wallets are less secure for storing secret keys, they are convenient for testing purposes. +// Keystore files offer a higher level of security. + +// #### Saving the Account to a PEM File +// ```js +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); + + const account = new Account(secretKey); + account.saveToPem({ path: path.resolve("wallet.pem") }); +} +// ``` + +// #### Saving the Account to a Keystore File +// ```js +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); + + const account = new Account(secretKey); + account.saveToKeystore({ + path: path.resolve("keystoreWallet.json"), + password: "password" + }); +} + +// ``` + +// ### Using a Ledger Device + +// You can manage your account with a Ledger device, allowing you to sign both transactions and messages while keeping your keys secure. + +// Note: **The multiversx-sdk package does not include Ledger support by default. To enable it, install the package with Ledger dependencies**: +/* // md-ignore +// ```bash +npm install @multiversx/sdk-hw-provider +// ``` +*/ // md-ignore + +// #### Creating a Ledger Account +// This can be done using the dedicated library. You can find more information [here](https://docs.multiversx.com/sdk-and-tools/sdk-js/sdk-js-signing-providers/#the-hardware-wallet-provider). + +// When signing transactions or messages, the Ledger device will prompt you to confirm the details before proceeding. + +// ### Compatibility with IAccount Interface + +// The `Account` implements the `IAccount` interface, making it compatible with transaction controllers and any other component that expects this interface. diff --git a/cookbook/accountManagement.js b/cookbook/accountManagement.js new file mode 100644 index 00000000..b870f26e --- /dev/null +++ b/cookbook/accountManagement.js @@ -0,0 +1,258 @@ + +import { Account, Address, DevnetEntrypoint } from "@multiversx/sdk-core"; // md-ignore +import path from 'path'; // md-ignore + +// ### Account management + +// The account management controller and factory allow us to create transactions for managing accounts, such as: +// - Guarding and unguarding accounts +// - Saving key-value pairs in the account storage, on the blockchain. + +// To learn more about Guardians, please refer to the [official documentation](https://docs.multiversx.com/developers/built-in-functions/#setguardian). +// A guardian can also be set using the WebWallet, which leverages our hosted `Trusted Co-Signer Service`. Follow [this guide](https://docs.multiversx.com/wallet/web-wallet/#guardian) for step-by-step instructions on guarding an account using the wallet. + +// #### Guarding an account using the controller +// ```js +{ + // create the entrypoint and the account controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); + + // create the account to guard // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // we can use a trusted service that provides a guardian, or simply set another address we own or trust // md-as-comment + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await controller.createTransactionForSettingGuardian( + alice, + alice.getNonceThenIncrement(), + { + guardianAddress: guardian, + serviceID: "SelfOwnedAddress", // this is just an example // md-as-comment + }, + ); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Guarding an account using the factory +// ```js +{ + // create the entrypoint and the account management factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); + + // create the account to guard // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // we can use a trusted service that provides a guardian, or simply set another address we own or trust // md-as-comment + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await factory.createTransactionForSettingGuardian( + alice, + { + guardianAddress: guardian, + serviceID: "SelfOwnedAddress", // this is just an example // md-as-comment + }, + ); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// Once a guardian is set, we must wait **20 epochs** before it can be activated. After activation, all transactions sent from the account must also be signed by the guardian. + +// #### Activating the guardian using the controller +// ```js +{ + // create the entrypoint and the account controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); + + // create the account to guard // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForGuardingAccount( + alice, + alice.getNonceThenIncrement(), + ); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Activating the guardian using the factory +// ```js +{ + // create the entrypoint and the account factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); + + // create the account to guard // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForGuardingAccount( + alice.address, + ); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Unguarding the account using the controller +// ```js +{ + // create the entrypoint and the account controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); + + // create the account to unguard // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await controller.createTransactionForUnguardingAccount( + alice, + alice.getNonceThenIncrement(), + { + guardian: guardian + } + ); + + // the transaction should also be signed by the guardian before being sent otherwise it won't be executed // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Unguarding the guardian using the factory +// ```js +{ + // create the entrypoint and the account factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); + + // create the account to guard // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await factory.createTransactionForUnguardingAccount( + alice.address, + guardian + ); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Saving a key-value pair to an account using the controller +// You can find more information [here](https://docs.multiversx.com/developers/account-storage) regarding the account storage. + +// ```js +{ + // create the entrypoint and the account controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // creating the key-value pairs we want to save // md-as-comment + const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + + const transaction = await controller.createTransactionForSavingKeyValue( + alice, + alice.getNonceThenIncrement(), + { + keyValuePairs: keyValuePairs + } + ); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Saving a key-value pair to an account using the factory +// ```js +{ + // create the entrypoint and the account factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); + + // create the account to guard // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // creating the key-value pairs we want to save // md-as-comment + const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); + + const transaction = await factory.createTransactionForSavingKeyValue( + alice.address, { + keyValuePairs: keyValuePairs, + }); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` diff --git a/cookbook/addresses.js b/cookbook/addresses.js new file mode 100644 index 00000000..55b39d1e --- /dev/null +++ b/cookbook/addresses.js @@ -0,0 +1,85 @@ +import { Address, AddressComputer, AddressFactory, LibraryConfig } from "@multiversx/sdk-core"; // md-ignore + +// ## Addresses + +// Create an `Address` object from a bech32-encoded string: + +// ``` js +{ + // Create an Address object from a bech32-encoded string // md-as-comment + const address = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + + console.log("Address (bech32-encoded):", address.toBech32()); + console.log("Public key (hex-encoded):", address.toHex()); + console.log("Public key (hex-encoded):", Buffer.from(address.getPublicKey()).toString("hex")); +} + +// ``` + +// Here’s how you can create an address from a hex-encoded string using the MultiversX JavaScript SDK: +// If the HRP (human-readable part) is not provided, the SDK will use the default one ("erd"). + +// ``` js +{ + // Create an address from a hex-encoded string with a specified HRP // md-as-comment + const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "erd"); + + console.log("Address (bech32-encoded):", address.toBech32()); + console.log("Public key (hex-encoded):", address.toHex()); +} +// ``` + +// #### Create an address from a raw public key + +// ``` js +{ + const pubkey = Buffer.from("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "hex"); + const addressFromPubkey = new Address(pubkey, "erd"); +} +// ``` + +// #### Using an AddressFactory to create addresses +// AddressFactory allows creating addresses with a custom HRP, ensuring consistency across your application. + +// ``` js +{ + const factory = new AddressFactory("erd"); + + const address1 = factory.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const address2 = factory.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); + const address3 = factory.fromPublicKey(Buffer.from("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "hex")); +} +// ``` + +// #### Getting the shard of an address +// ``` js + +const addressComputer = new AddressComputer(); +console.log("Shard:", addressComputer.getShardOfAddress(address)); +// ``` + +// Checking if an address is a smart contract +// ``` js + +const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm"); +console.log("Is contract address:", contractAddress.isSmartContract()); +// ``` + +// ### Changing the default hrp +// The **LibraryConfig** class manages the default **HRP** (human-readable part) for addresses, which is set to `"erd"` by default. +// You can change the HRP when creating an address or modify it globally in **LibraryConfig**, affecting all newly created addresses. +// ``` js + +console.log(LibraryConfig.defaultAddressHrp); +const defaultAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); +console.log(defaultAddress.toBech32()); + +LibraryConfig.defaultAddressHrp = "test"; +const testAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); +console.log(testAddress.toBech32()); + +// Reset HRP back to "erd" to avoid affecting other parts of the application. // md-as-comment +LibraryConfig.defaultAddressHrp = "erd"; +// ``` + + diff --git a/cookbook/cookbook.md b/cookbook/cookbook.md new file mode 100644 index 00000000..844d154e --- /dev/null +++ b/cookbook/cookbook.md @@ -0,0 +1,3168 @@ +## Overview + +This guide walks you through handling common tasks using the MultiversX Javascript SDK (v14, latest stable version). + +## Creating an Entrypoint + +An Entrypoint represents a network client that simplifies access to the most common operations. +There is a dedicated entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint`, `LocalnetEntrypoint`. + +For example, to create a Devnet entrypoint you have the following command: + +```js +const entrypoint = new DevnetEntrypoint(); +``` + +#### Using a Custom API +If you'd like to connect to a third-party API, you can specify the url parameter: + +```js +const apiEntrypoint = new DevnetEntrypoint({ url: 'https://custom-multiversx-devnet-api.com' }); +``` + +#### Using a Proxy + +By default, the DevnetEntrypoint uses the standard API. However, you can create a custom entrypoint that interacts with a proxy by specifying the kind parameter: + + +```js +const customEntrypoint = new DevnetEntrypoint({ + url: 'https://devnet-gateway.multiversx.com', + kind: 'proxy' +}); +``` + +## Creating Accounts + +You can initialize an account directly from the entrypoint. Keep in mind that the account is network agnostic, meaning it doesn't matter which entrypoint is used. +Accounts are used for signing transactions and messages and managing the account's nonce. They can also be saved to a PEM or keystore file for future use. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const account = entrypoint.createAccount(); +} +``` + +### Other Ways to Instantiate an Account + +#### From a Secret Key +```js +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); + + const accountFromSecretKey = new Account(secretKey); +} +``` + +#### From a PEM file +```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const accountFromPem = Account.newFromPem(filePath); +} +``` + +#### From a Keystore File +```js +{ + const keystorePath = path.join("src", "testdata", "testwallets", "alice.json"); + const accountFromKeystore = Account.newFromKeystore({ + filePath: keystorePath, + password: "password" + }); +} +``` + +#### From a Mnemonic +```js + +const mnemonic = Mnemonic.generate(); +const accountFromMnemonic = Account.newFromMnemonic(mnemonic.getText()); +``` + +#### From a KeyPair + +```js +const keypair = KeyPair.generate(); +const accountFromKeyPairs = Account.newFromKeypair(keypair); +``` + +### Managing the Account Nonce + +An account has a `nonce` property that the user is responsible for managing. +You can fetch the nonce from the network and increment it after each transaction. +Each transaction must have the correct nonce, otherwise it will fail to execute. + +```js +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const key = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); + + const accountWithNonce = new Account(key); + const entrypoint = new DevnetEntrypoint(); + + // Fetch the current nonce from the network + accountWithNonce.nonce = await entrypoint.recallAccountNonce(accountWithNonce.address); + + // Create and send a transaction here... + + // Increment nonce after each transaction + const nonce = accountWithNonce.getNonceThenIncrement(); +} +``` + +For more details, see the [Creating Transactions](#creating-transactions) section. + +#### Saving the Account to a File + +Accounts can be saved to either a PEM file or a keystore file. +While PEM wallets are less secure for storing secret keys, they are convenient for testing purposes. +Keystore files offer a higher level of security. + +#### Saving the Account to a PEM File +```js +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); + + const account = new Account(secretKey); + account.saveToPem({ path: path.resolve("wallet.pem") }); +} +``` + +#### Saving the Account to a Keystore File +```js +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); + + const account = new Account(secretKey); + account.saveToKeystore({ + path: path.resolve("keystoreWallet.json"), + password: "password" + }); +} + +``` + +### Using a Ledger Device + +You can manage your account with a Ledger device, allowing you to sign both transactions and messages while keeping your keys secure. + +Note: **The multiversx-sdk package does not include Ledger support by default. To enable it, install the package with Ledger dependencies**: +```bash +npm install @multiversx/sdk-hw-provider +``` + +#### Creating a Ledger Account +This can be done using the dedicated library. You can find more information [here](/sdk-and-tools/sdk-js/sdk-js-signing-providers/#the-hardware-wallet-provider). + +When signing transactions or messages, the Ledger device will prompt you to confirm the details before proceeding. + +### Compatibility with IAccount Interface + +The `Account` implements the `IAccount` interface, making it compatible with transaction controllers and any other component that expects this interface. + +## Calling the Faucet + +This functionality is not yet available through the entrypoint, but we recommend using the faucet available within the Web Wallet. For more details about the faucet [see this](/wallet/web-wallet/#testnet-and-devnet-faucet). + +- [Testnet Wallet](https://testnet-wallet.multiversx.com/). +- [Devnet Wallet](https://devnet-wallet.multiversx.com/). + +### Interacting with the network + +The entrypoint exposes a few ways to directly interact with the network, such as: + +- `recallAccountNonce(address: Address): Promise;` +- `sendTransactions(transactions: Transaction[]): Promise<[number, string[]]>;` +- `sendTransaction(transaction: Transaction): Promise;` +- `getTransaction(txHash: string): Promise;` +- `awaitCompletedTransaction(txHash: string): Promise;` + +Some other methods are exposed through a so called **network provider**. + +- **ApiNetworkProvider**: Interacts with the API, which is a layer over the proxy. It fetches data from the network and `Elastic Search`. +- **ProxyNetworkProvider**: Interacts directly with the proxy of an observing squad. + +To get the underlying network provider from our entrypoint, we can do as follows: + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); +} +``` + +### Creating a network provider +When manually instantiating a network provider, you can provide a configuration to specify the client name and set custom request options. + +```js +{ + // Create a configuration object + const config = { + clientName: "hello-multiversx", + requestsOptions: { + timeout: 1000, // Timeout in milliseconds + auth: { + username: "user", + password: "password" + } + } + }; + + // Instantiate the network provider with the config + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com", config); +} +``` + +A full list of available methods for `ApiNetworkProvider` can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/classes/ApiNetworkProvider.html). + +Both `ApiNetworkProvider` and `ProxyNetworkProvider` implement a common interface, which can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/interfaces/INetworkProvider.html). This allows them to be used interchangeably. + +The classes returned by the API expose the most commonly used fields directly for convenience. However, each object also contains a `raw` field that stores the original API response, allowing access to additional fields if needed. + + +## Fetching data from the network + +### Fetching the network config + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); + + const networkConfig = entrypoint.getNetworkConfig(); +} +``` + +### Fetching the network status + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); + + const metaNetworkStatus = entrypoint.getNetworkStatus(); // fetches status from metachain + const networkStatus = entrypoint.getNetworkStatus(1); // fetches status from shard one +} +``` + +### Fetching a Block from the Network +To fetch a block, we first instantiate the required arguments and use its hash. The API only supports fetching blocks by hash, whereas the **PROXY** allows fetching blocks by either hash or nonce. + +When using the **PROXY**, keep in mind that the shard must also be specified in the arguments. + +#### Fetching a block using the **API** +```js +{ + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); + const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; + const block = await api.getBlock(blockHash); +} +``` + +Additionally, we can fetch the latest block from the network: + +```js +{ + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); + const latestBlock = await api.getLatestBlock(); +} +``` + +#### Fetching a block using the **PROXY** + +When using the proxy, we have to provide the shard, as well. +```js +{ + const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); + const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; + const block = proxy.getBlock({ blockHash, shard: 1 }); +} +``` + +We can also fetch the latest block from the network. +By default, the shard will be the metachain, but we can specify a different shard if needed. + +```js +{ + const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); + const latestBlock = proxy.getLatestBlock(); +} +``` + +### Fetching an Account +To fetch an account, we need its address. Once we have the address, we create an `Address` object and pass it as an argument to the method. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccount(alice); +} +``` + +### Fetching an Account's Storage +We can also fetch an account's storage, allowing us to retrieve all key-value pairs saved for that account. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccountStorage(alice); +} +``` + +If we only want to fetch a specific key, we can do so as follows: + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccountStorageEntry(alice, "testKey"); +} +``` + +### Waiting for an Account to Meet a Condition +There are times when we need to wait for a specific condition to be met before proceeding with an action. +For example, let's say we want to send 7 EGLD from Alice to Bob, but this can only happen once Alice's balance reaches at least 7 EGLD. +This approach is useful in scenarios where you're waiting for external funds to be sent to Alice, enabling her to transfer the required amount to another recipient. + +To implement this, we need to define the condition to check each time the account is fetched from the network. We create a function that takes an `AccountOnNetwork` object as an argument and returns a `bool`. +Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const condition = (account) => { + return account.balance >= 7000000000000000000; // 7 EGLD + }; + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.awaitAccountOnCondition(alice, condition); +} +``` + +### Sending and Simulating Transactions +To execute transactions, we use the network providers to broadcast them to the network. Keep in mind that for transactions to be processed, they must be signed. + +#### Sending a Transaction + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = new Transaction({ + sender: alice, + receiver: bob, + gasLimit: 50000n, + chainID: "D", + }); + + // set the correct nonce and sign the transaction ... + + const transactionHash = await api.sendTransaction(transaction); +} +``` + +#### Sending multiple transactions +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const firstTransaction = new Transaction({ + sender: alice, + receiver: bob, + gasLimit: 50000n, + chainID: "D", + nonce: 2 + }); + + const secondTransaction = new Transaction({ + sender: bob, + receiver: alice, + gasLimit: 50000n, + chainID: "D", + nonce: 1, + }); + + const thirdTransaction = new Transaction({ + sender: alice, + receiver: alice, + gasLimit: 60000n, + chainID: "D", + nonce: 3, + data: new Uint8Array(Buffer.from("hello")) + }); + + // set the correct nonce and sign the transaction ... + + const [numOfSentTxs, hashes] = await api.sendTransactions([firstTransaction, secondTransaction, thirdTransaction]); +} +``` + +#### Simulating transactions +A transaction can be simulated before being sent for processing by the network. This is primarily used for smart contract calls, allowing you to preview the results produced by the smart contract. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); + + const transaction = new Transaction({ + sender: alice, + receiver: contract, + gasLimit: 5000000n, + chainID: "D", + data: new Uint8Array(Buffer.from("add@07")) + }); + + const transactionOnNetwork = await api.simulateTransaction(transaction); +} +``` + +#### Estimating the gas cost of a transaction +Before sending a transaction to the network for processing, you can retrieve the estimated gas limit required for the transaction to be executed. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); + + const nonce = await entrypoint.recallAccountNonce(alice); + + const transaction = new Transaction({ + sender: alice, + receiver: contract, + gasLimit: 5000000, + chainID: "D", + data: new Uint8Array(Buffer.from("add@07")), + nonce: nonce + }); + + const transactionCostResponse = await api.estimateTransactionCost(transaction); +} +``` + +### Waiting for transaction completion +After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); +} +``` + +### Waiting for a Transaction to Satisfy a Condition +Similar to accounts, we can wait until a transaction meets a specific condition. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const condition = (txOnNetwork) => !txOnNetwork.status.isSuccessful(); + + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionOnCondition(txHash, condition); +} +``` + +### Waiting for transaction completion +After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); +} +``` + +### Fetching Transactions from the Network +After sending a transaction, we can fetch it from the network using the transaction hash, which we receive after broadcasting the transaction. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.getTransaction(txHash); +} +``` + +### Fetching a token from an account +We can fetch a specific token (ESDT, MetaESDT, SFT, NFT) from an account by providing the account's address and the token identifier. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + let token = new Token({ identifier: "TEST-ff155e" }); // ESDT + let tokenOnNetwork = await api.getTokenOfAccount(alice, token); + + + token = new Token({ identifier: "NFT-987654", nonce: 11n }); // NFT + tokenOnNetwork = await api.getTokenOfAccount(alice, token); +} +``` + +### Fetching all fungible tokens of an account +Fetches all fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const fungibleTokens = await api.getFungibleTokensOfAccount(alice); +} +``` + +### Fetching all non-fungible tokens of an account +Fetches all non-fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const nfts = await api.getNonFungibleTokensOfAccount(alice); +} +``` + +### Fetching token metadata +If we want to fetch the metadata of a token (e.g., owner, decimals, etc.), we can use the following methods: + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + // used for ESDT + const fungibleTokenDefinition = await api.getDefinitionOfFungibleToken("TEST-ff155e"); + + // used for METAESDT, SFT, NFT + const nonFungibleTokenDefinition = await api.getDefinitionOfTokenCollection("NFTEST-ec88b8"); +} +``` + +### Querying Smart Contracts +Smart contract queries, or view functions, are endpoints that only read data from the contract. To send a query to the observer nodes, we can proceed as follows: + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const query = new SmartContractQuery({ + contract: Address.newFromBech32("erd1qqqqqqqqqqqqqpgqqy34h7he2ya6qcagqre7ur7cc65vt0mxrc8qnudkr4"), + function: "getSum", + arguments: [], + }); + const response = await api.queryContract(query); +} +``` + +### Custom Api/Proxy calls +The methods exposed by the `ApiNetworkProvider` or `ProxyNetworkProvider` are the most common and widely used. However, there may be times when custom API calls are needed. For these cases, we’ve created generic methods for both GET and POST requests. +Let’s assume we want to retrieve all the transactions sent by Alice in which the `delegate` function was called. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const url = `transactions/${alice.toBech32()}?function=delegate`; + + const response = await api.doGetGeneric(url); +} +``` + +## Creating transactions + +In this section, we’ll explore how to create different types of transactions. To create transactions, we can use either controllers or factories. +Controllers are ideal for quick scripts or network interactions, while factories provide a more granular and lower-level approach, typically required for DApps. + +Controllers typically use the same parameters as factories, but they also require an Account object and the sender’s nonce. +Controllers also include extra functionality, such as waiting for transaction completion and parsing transactions. +The same functionality can be achieved for transactions built using factories, and we’ll see how in the sections below. In the next section, we’ll learn how to create transactions using both methods. + +### Instantiating Controllers and Factories +There are two ways to create controllers and factories: +1. Get them from the entrypoint. +2. Manually instantiate them. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + + // getting the controller and the factory from the entrypoint + const transfersController = entrypoint.createTransfersController(); + const transfersFactory = entrypoint.createTransfersTransactionsFactory(); + + // manually instantiating the controller and the factory + const controller = new TransfersController({ chainID: 'D' }); + + const config = new TransactionsFactoryConfig({ chainID: 'D' }); + const factory = new TransferTransactionsFactory({ config }); +} +``` + +### Token transfers +We can send both native tokens (EGLD) and ESDT tokens using either the controller or the factory. +#### Native Token Transfers Using the Controller +When using the controller, the transaction will be signed because we’ll be working with an Account. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transfersController = entrypoint.createTransfersController(); + const transaction = await transfersController.createTransactionForTransfer( + alice, + alice.getNonceThenIncrement(), + { + receiver: bob, + nativeAmount: 1n, + }, + ); + + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. + +#### Native Token Transfers Using the Factory +When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. +You will need to handle these aspects after the transaction is created. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTransfersTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = factory.createTransactionForTransfer(alice, { + receiver: bob, + nativeAmount: 1000000000000000000n, + }); + + // set the sender's nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction using the sender's account + transaction.signature = await alice.signTransaction(transaction); + + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. + +#### Custom token transfers using the controller + +```js +{ + const entrypoint = new DevnetEntrypoint(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const esdt = new Token({ identifier: "TEST-123456" }); + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); + + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); + + const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); + const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); + + const transfersController = entrypoint.createTransfersController(); + const transaction = transfersController.createTransactionForTransfer(alice, alice.getNonceThenIncrement(), { + receiver: bob, + tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], + }); + + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. + +#### Custom token transfers using the factory +When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. These aspects should be handled after the transaction is created. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTransfersTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const esdt = new Token({ identifier: "TEST-123456" }); // fungible tokens don't have a nonce + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); // we set the desired amount we want to send + + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); // for NFTs we set the amount to `1` + + const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); + const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); // for SFTs we set the desired amount we want to send + + const transaction = factory.createTransactionForTransfer(alice, { + receiver: bob, + tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], + }); + + // set the sender's nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction using the sender's account + transaction.signature = await alice.signTransaction(transaction); + + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. + +#### Sending native and custom tokens +Both native and custom tokens can now be sent. If a `nativeAmount` is provided along with `tokenTransfers`, the native token will be included in the `MultiESDTNFTTransfer` built-in function call. +We can send both types of tokens using either the `controller` or the `factory`, but for simplicity, we’ll use the controller in this example. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const esdt = new Token({ identifier: "TEST-123456" }); + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); + + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); + + const transfersController = entrypoint.createTransfersController(); + const transaction = transfersController.createTransactionForTransfer(alice, alice.getNonceThenIncrement(), { + receiver: bob, + nativeAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer], + }); + + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +### Smart Contracts + +#### Contract ABIs + +A contract's ABI (Application Binary Interface) describes the endpoints, data structures, and events that the contract exposes. +While interactions with the contract are possible without the ABI, they are much easier to implement when the definitions are available. + +#### Loading the ABI from a file +```js +{ + let abiJson = await promises.readFile("../contracts/adder.abi.json", { encoding: "utf8" }); + let abiObj = JSON.parse(abiJson); + let abi = AbiRegistry.create(abiObj); +} +``` + +#### Loading the ABI from an URL + +```js +{ + const response = await axios.get("https://github.com/multiversx/mx-sdk-js-core/raw/main/src/testdata/adder.abi.json"); + abi = AbiRegistry.create(response.data); +} +``` + +#### Manually construct the ABI + +If an ABI file isn’t available, but you know the contract’s endpoints and data types, you can manually construct the ABI. + +```js +{ + abi = AbiRegistry.create({ + "endpoints": [{ + "name": "add", + "inputs": [], + "outputs": [] + }] + }); +} +``` + +```js +{ + abi = AbiRegistry.create({ + "endpoints": [ + { + "name": "foo", + "inputs": [ + { "type": "BigUint" }, + { "type": "u32" }, + { "type": "Address" } + ], + "outputs": [ + { "type": "u32" } + ] + }, + { + "name": "bar", + "inputs": [ + { "type": "counted-variadic" }, + { "type": "variadic" } + ], + "outputs": [] + } + ] + }); +} +``` + +### Smart Contract deployments +For creating smart contract deployment transactions, we have two options: a controller and a factory. Both function similarly to the ones used for token transfers. +When creating transactions that interact with smart contracts, it's recommended to provide the ABI file to the controller or factory if possible. +This allows arguments to be passed as native Javascript values. If the ABI is not available, but we know the expected data types, we can pass arguments as typed values (e.g., `BigUIntValue`, `ListValue`, `StructValue`, etc.) or as raw bytes. + +#### Deploying a Smart Contract Using the Controller + +```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); + + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); + + // load the contract bytecode + const bytecode = await promises.readFile("../contracts/adder.wasm"); + // load the abi file + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + + const controller = entrypoint.createSmartContractController(abi); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; + + const deployTransaction = await controller.createTransactionForDeploy( + sender, + sender.getNonceThenIncrement(), + { + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }, + ); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(deployTransaction); +} +``` + +:::tip +When creating transactions using [`SmartContractController`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/SmartContractController.html) or [`SmartContractTransactionsFactory`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/SmartContractTransactionsFactory.html), even if the ABI is available and provided, +you can still use [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/TypedValue.html) objects as arguments for deployments and interactions. + +Even further, you can use a mix of [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/TypedValue.html) objects and plain JavaScript values and objects. For example: + +```js +let args = [new U32Value(42), "hello", { foo: "bar" }, new TokenIdentifierValue("TEST-abcdef")]; +``` + +::: + +#### Parsing contract deployment transactions + +```js +{ + // We use the transaction hash we got when broadcasting the transaction + const outcome = await controller.awaitCompletedDeploy(txHash); // waits for transaction completion and parses the result + const contractAddress = outcome.contracts[0].address; +} +``` + +If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: + +```js +{ + // We use the transaction hash we got when broadcasting the transaction + // If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: + const transactionOnNetwork = await controller.awaitTransactionCompleted(txHash); + + // parsing the transaction + const outcome = await controller.parseDeploy(transactionOnNetwork); +} +``` + +#### Computing the contract address + +Even before broadcasting, at the moment you know the sender's address and the nonce for your deployment transaction, you can (deterministically) compute the (upcoming) address of the smart contract: + +```js +{ + const addressComputer = new AddressComputer(); + const contractAddress = addressComputer.computeContractAddress( + deployTransaction.sender, + deployTransaction.nonce + ); + + console.log("Contract address:", contractAddress.toBech32()); +} +``` + +#### Deploying a Smart Contract using the factory +After the transaction is created the nonce needs to be properly set and the transaction should be signed before broadcasting it. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTransfersTransactionsFactory(); + + // load the contract bytecode + const bytecode = await promises.readFile("../contracts/adder.wasm"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args = [new BigUIntValue(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const deployTransaction = await factory.createTransactionForDeploy( + sender, + { + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }, + ); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(sender.address); + + // set the nonce + deployTransaction.nonce = alice.nonce; + + // sign the transaction + deployTransaction.signature = await alice.signTransaction(transaction); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(deployTransaction); + + // waiting for transaction to complete + const transactionOnNetwork = await entrypoint.awaitTransactionCompleted(txHash); + + // parsing transaction + const parser = new SmartContractTransactionsOutcomeParser(); + const parsedOutcome = parser.parseDeploy(transactionOnNetwork); + const contractAddress = parsedOutcome.contracts[0].address; + + console.log(contractAddress.toBech32()); +} +``` + +### Smart Contract calls + +In this section we'll see how we can call an endpoint of our previously deployed smart contract using both approaches with the `controller` and the `factory`. + +#### Calling a smart contract using the controller + +```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); + + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); + + // load the abi file + const abi = await loadAbiRegistry("src/testdata/adder.abi.json");; + const controller = entrypoint.createSmartContractController(abi); + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; + + const transaction = await controller.createTransactionForExecute( + sender, + sender.getNonceThenIncrement(), + { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + }, + ); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + console.log(txHash); +} +``` + +#### Parsing smart contract call transactions +In our case, calling the add endpoint does not return anything, but similar to the example above, we could parse this transaction to get the output values of a smart contract call. + +```js +{ + // waits for transaction completion and parses the result + const parsedOutcome = controller.awaitCompletedExecute(transactionOnNetwork); + const values = parsedOutcome.contracts.values; +} +``` + +#### Calling a smart contract and sending tokens (transfer & execute) +Additionally, if an endpoint requires a payment when called, we can send tokens to the contract while creating a smart contract call transaction. +Both EGLD and ESDT tokens or a combination of both can be sent. This functionality is supported by both the controller and the factory. + +```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); + + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); + + // load the abi file + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + + // get the smart contracts controller + const controller = entrypoint.createSmartContractController(abi); + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; + + // creating the transfers + const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10 }); + const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); + + const secondToken = new Token({ identifier: "BAR-c80d29" }); + const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); + + const transaction = await controller.createTransactionForExecute( + sender, + sender.getNonceThenIncrement(), + { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + nativeTransferAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer] + }, + ); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + console.log(txHash); +} +``` + +#### Calling a smart contract using the factory +Let's create the same smart contract call transaction, but using the `factory`. + +```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // load the abi file + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + + // get the smart contracts controller + const controller = entrypoint.createSmartContractTransactionsFactory(abi); + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; + + // creating the transfers + const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10 }); + const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); + + const secondToken = new Token({ identifier: "BAR-c80d29" }); + const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); + + const transaction = await controller.createTransactionForExecute( + sender, + { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + nativeTransferAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer] + }, + ); + + transaction.nonce = alice.getNonceThenIncrement(); + transaction.signature = await alice.signTransaction(transaction); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + console.log(txHash); +} +``` + +#### Parsing transaction outcome +As said before, the `add` endpoint we called does not return anything, but we could parse the outcome of smart contract call transactions, as follows: + +```js +{ + // load the abi file + const entrypoint = new DevnetEntrypoint(); + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + const parser = SmartContractTransactionsOutcomeParser({ abi }); + const transactionOnNetwork = entrypoint.getTransaction(txHash); + const outcome = parser.parseExecute(transactionOnNetwork); +} +``` + +#### Decoding transaction events +You might be interested into decoding events emitted by a contract. You can do so by using the `TransactionEventsParser`. + +Suppose we'd like to decode a `startPerformAction` event emitted by the [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract. + +First, we load the abi file, then we fetch the transaction, we extract the event from the transaction and then we parse it. + +```js +{ + // load the abi files + const entrypoint = new DevnetEntrypoint(); + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + const parser = new TransactionEventsParser({ abi }); + const transactionOnNetwork = entrypoint.getTransaction(txHash); + const events = gatherAllEvents(transactionOnNetwork); + const outcome = parser.parseEvents({ events }); +} +``` + +#### Decoding transaction events +Whenever needed, the contract ABI can be used for manually encoding or decoding custom types. + +Let's encode a struct called EsdtTokenPayment (of [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract) into binary data. +```js +{ + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + const paymentType = abi.getStruct("EsdtTokenPayment"); + + const paymentStruct = new Struct(paymentType, [ + new Field(new TokenIdentifierValue("TEST-8b028f"), "token_identifier"), + new Field(new U64Value(0n), "token_nonce"), + new Field(new BigUIntValue(10000n), "amount") + ]); + + const encoded = codec.encodeNested(paymentStruct); + + console.log(encoded.toString("hex")); +} +``` + +Now let's decode a struct using the ABI. +```js +{ + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + const actionStructType = abi.getEnum("Action"); + const data = Buffer.from("0500000000000000000500d006f73c4221216fa679bc559005584c4f1160e569e1000000012a0000000003616464000000010000000107", "hex"); + + const [decoded] = codec.decodeNested(data, actionStructType); + const decodedValue = decoded.valueOf(); + console.log(JSON.stringify(decodedValue, null, 4)); +} +``` + +### Smart Contract queries +When querying a smart contract, a **view function** is called. A view function does not modify the state of the contract, so we do not need to send a transaction. +To perform this query, we use the **SmartContractController**. While we can use the contract's ABI file to encode the query arguments, we can also use it to parse the result. +In this example, we will query the **adder smart contract** by calling its `getSum` endpoint. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + + // create the controller + const controller = entrypoint.createSmartContractController(abi); + + // creates the query, runs the query, parses the result + const response = await controller.query({ contract: contractAddress, function: "getSum", arguments: [] }); +} +``` + +If we need more granular control, we can split the process into three steps: **create the query, run the query, and parse the query response**. +This approach achieves the same result as the previous example. + +```js +{ + const entrypoint = new DevnetEntrypoint(); + + // load the abi + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + + // the contract address we'll query + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + // create the controller + const controller = entrypoint.createSmartContractController(abi); + + // create the query + const query = await controller.createQuery({ contract: contractAddress, function: "getSum", arguments: [] }); + // runs the query + const response = await controller.runQuery(query); + + // parse the result + const parsedResponse = controller.parseQueryResponse(response); +} +``` + +### Upgrading a smart contract +Contract upgrade transactions are similar to deployment transactions (see above) because they also require contract bytecode. +However, in this case, the contract address is already known. Like deploying a smart contract, we can upgrade a smart contract using either the **controller** or the **factory**. + +#### Uprgrading a smart contract using the controller +```js +{ + // prepare the account + const entrypoint = new DevnetEntrypoint(); + const keystorePath = path.join("src", "testdata", "testwallets", "alice.json"); + const account = Account.newFromKeystore({ + filePath: keystorePath, + password: "password" + }); + // the developer is responsible for managing the nonce + account.nonce = entrypoint.recall_account_nonce(account.address); + + // load the abi + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + + // create the controller + const controller = entrypoint.createSmartContractController(abi); + + // load the contract bytecode; this is the new contract code, the one we want to upgrade to + const bytecode = await promises.readFile("../contracts/adder.wasm"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + const upgradeTransaction = await controller.createTransactionForUpgrade( + sender, + sender.getNonceThenIncrement(), + { + contract: contractAddress, + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }, + ); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(upgradeTransaction); + + console.log({ txHash }); +} +``` + +### Token management + +In this section, we're going to create transactions to issue fungible tokens, issue semi-fungible tokens, create NFTs, set token roles, but also parse these transactions to extract their outcome (e.g. get the token identifier of the newly issued token). + +These methods are available through the `TokenManagementController` and the `TokenManagementTransactionsFactory`. +The controller also includes built-in methods for awaiting the completion of transactions and parsing their outcomes. +For the factory, the same functionality can be achieved using the `TokenManagementTransactionsOutcomeParser`. + +For scripts or quick network interactions, we recommend using the controller. However, for a more granular approach (e.g., DApps), the factory is the better choice. + +#### Issuing fungible tokens using the controller +```js +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }, + ); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + const outcome = await entrypoint.awaitCompletedIssueFungible(txHash); + + const tokenIdentifier = outcome[0].tokenIdentifier; + +} +``` + +#### Issuing fungible tokens using the factory +```js +{ + // create the entrypoint and the token management transactions factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForIssuingFungible( + alice, + { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }, + ); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueFungible(transactionOnNetwork); + const tokenIdentifier = outcome[0].tokenIdentifier; +} +``` + + +#### Setting special roles for fungible tokens using the controller +```js +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await controller.createTransactionForSettingSpecialRoleOnFungibleToken( + alice, + alice.getNonceThenIncrement(), + { + user: bob, + tokenIdentifier: "TEST-123456", + addRoleLocalMint: true, + addRoleLocalBurn: true, + addRoleESDTTransferRole: true, + }, + ); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + const outcome = await entrypoint.awaitCompletedSetSpecialRoleOnFungibleToken(transaction); + + const roles = outcome[0].roles; + const user = outcome[0].userAddress; +} +``` + +#### Setting special roles for fungible tokens using the factory +```js +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await factory.createTransactionForIssuingFungible( + alice, + { + user: bob, + tokenIdentifier: "TEST-123456", + addRoleLocalMint: true, + addRoleLocalBurn: true, + addRoleESDTTransferRole: true, + }, + ); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseSetSpecialRole(transactionOnNetwork); + + const roles = outcome[0].roles; + const user = outcome[0].userAddress; +} +``` + +#### Issuing semi-fungible tokens using the controller +```js +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingSemiFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWSEMI", + tokenTicker: "SEMI", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }, + ); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + const outcome = await entrypoint.awaitCompletedIssueSemiFungible(txHash); + + const tokenIdentifier = outcome[0].tokenIdentifier; +} +``` + +#### Issuing semi-fungible tokens using the factory +```js +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForIssuingSemiFungible( + alice, + { + tokenName: "NEWSEMI", + tokenTicker: "SEMI", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }, + ); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueSemiFungible(transactionOnNetwork); + + const tokenIdentifier = outcome[0].tokenIdentifier; +} +``` + +#### Issuing NFT collection & creating NFTs using the controller + +```js +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.creatTokenManagementController(); + + // create the issuer of the token + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + let transaction = await controller.createTransactionForIssuingNonFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWNFT", + tokenTicker: "NFT", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }, + ); + + // sending the transaction + let txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + let outcome = await entrypoint.awaitCompletedIssueNonFungible(txHash); + + const collectionIdentifier = outcome[0].tokenIdentifier; + + // create an NFT + transaction = controller.createTransactionForCreatingNft(alice, + alice.getNonceThenIncrement(), + { + tokenIdentifier: "FRANK-aa9e8d", + initialQuantity: 1n, + name: "test", + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + }, + ); + + // sending the transaction + txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + outcome = await entrypoint.awaitCompletedCreateNft(txHash); + + const identifier = outcome[0].tokenIdentifier; + const nonce = outcome[0].nonce; + const initialQuantity = outcome[0].initialQuantity; + +} +``` + +#### Issuing NFT collection & creating NFTs using the factory +```js +{ + // create the entrypoint and the token management transdactions factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + let transaction = await factory.createTransactionForIssuingNonFungible( + alice, + { + tokenName: "NEWNFT", + tokenTicker: "NFT", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }, + ); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + let txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + let transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier + let parser = new TokenManagementTransactionsOutcomeParser(); + let outcome = parser.parseIssueNonFungible(transactionOnNetwork); + + const collectionIdentifier = outcome[0].tokenIdentifier; + + transaction = await factory.createTransactionForCreatingNFT( + alice, + { + tokenIdentifier: "FRANK-aa9e8d", + initialQuantity: 1n, + name: "test", + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + }, + ); + + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + txHash = await entrypoint.sendTransaction(transaction); + + // ### wait for transaction to execute, extract the token identifier + transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + outcome = parser.parseIssueNonFungible(transactionOnNetwork); + + const identifier = outcome[0].tokenIdentifier; + const nonce = outcome[0].nonce; + const initialQuantity = outcome[0].initialQuantity; +} +``` + +These are just a few examples of what you can do using the token management controller or factory. For a complete list of supported methods, please refer to the autogenerated documentation: + +- [TokenManagementController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementController.html) +- [TokenManagementTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementTransactionsFactory.html) + +### Account management + +The account management controller and factory allow us to create transactions for managing accounts, such as: +- Guarding and unguarding accounts +- Saving key-value pairs in the account storage, on the blockchain. + +To learn more about Guardians, please refer to the [official documentation](/developers/built-in-functions/#setguardian). +A guardian can also be set using the WebWallet, which leverages our hosted `Trusted Co-Signer Service`. Follow [this guide](/wallet/web-wallet/#guardian) for step-by-step instructions on guarding an account using the wallet. + +#### Guarding an account using the controller +```js +{ + // create the entrypoint and the account controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); + + // create the account to guard + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // we can use a trusted service that provides a guardian, or simply set another address we own or trust + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await controller.createTransactionForSettingGuardian( + alice, + alice.getNonceThenIncrement(), + { + guardianAddress: guardian, + serviceID: "SelfOwnedAddress", // this is just an example + }, + ); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Guarding an account using the factory +```js +{ + // create the entrypoint and the account management factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); + + // create the account to guard + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // we can use a trusted service that provides a guardian, or simply set another address we own or trust + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await factory.createTransactionForSettingGuardian( + alice, + { + guardianAddress: guardian, + serviceID: "SelfOwnedAddress", // this is just an example + }, + ); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +Once a guardian is set, we must wait **20 epochs** before it can be activated. After activation, all transactions sent from the account must also be signed by the guardian. + +#### Activating the guardian using the controller +```js +{ + // create the entrypoint and the account controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); + + // create the account to guard + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForGuardingAccount( + alice, + alice.getNonceThenIncrement(), + ); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Activating the guardian using the factory +```js +{ + // create the entrypoint and the account factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); + + // create the account to guard + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForGuardingAccount( + alice.address, + ); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Unguarding the account using the controller +```js +{ + // create the entrypoint and the account controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); + + // create the account to unguard + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await controller.createTransactionForUnguardingAccount( + alice, + alice.getNonceThenIncrement(), + { + guardian: guardian + } + ); + + // the transaction should also be signed by the guardian before being sent otherwise it won't be executed + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Unguarding the guardian using the factory +```js +{ + // create the entrypoint and the account factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); + + // create the account to guard + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await factory.createTransactionForUnguardingAccount( + alice.address, + guardian + ); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Saving a key-value pair to an account using the controller +You can find more information [here](/developers/account-storage) regarding the account storage. + +```js +{ + // create the entrypoint and the account controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // creating the key-value pairs we want to save + const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + + const transaction = await controller.createTransactionForSavingKeyValue( + alice, + alice.getNonceThenIncrement(), + { + keyValuePairs: keyValuePairs + } + ); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Saving a key-value pair to an account using the factory +```js +{ + // create the entrypoint and the account factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); + + // create the account to guard + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // creating the key-value pairs we want to save + const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); + + const transaction = await factory.createTransactionForSavingKeyValue( + alice.address, { + keyValuePairs: keyValuePairs, + }); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +### Delegation management + +To learn more about staking providers and delegation, please refer to the official [documentation](/validators/delegation-manager/#introducing-staking-providers). +In this section, we'll cover how to: +- Create a new delegation contract +- Retrieve the contract address +- Delegate funds to the contract +- Redelegate rewards +- Claim rewards +- Undelegate and withdraw funds + +These operations can be performed using both the controller and the **factory**. For a complete list of supported methods, please refer to the autogenerated documentation: +- [DelegationController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationController.html) +- [DelegationTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationTransactionsFactory.html) + +#### Creating a New Delegation Contract Using the Controller +```js +{ + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForNewDelegationContract( + alice.address, + alice.getNonceThenIncrement(), + { + totalDelegationCap: 0, + serviceFee: 10n, + amount: 1250000000000000000000n, + }); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction completion, extract delegation contract's address + const outcome = await controller.awaitCompletedCreateNewDelegationContract(txHash); + + const contractAddress = outcome[0].contractAddress; +} +``` + +#### Creating a new delegation contract using the factory +```js +{ + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + + const transaction = await factory.createTransactionForNewDelegationContract(alice.address, + { + totalDelegationCap: 0, + serviceFee: 10n, + amount: 1250000000000000000000n, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // waits until the transaction is processed and fetches it from the network + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the contract address + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueFungible(transactionOnNetwork); + const contractAddress = outcome[0].contractAddress; +} +``` + +#### Delegating funds to the contract using the Controller +We can send funds to a delegation contract to earn rewards. + +```js +{ + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await controller.createTransactionForDelegating( + alice.address, + alice.getNonceThenIncrement(), + { + delegationContract: contract, + amount: 5000000000000000000000n, + }); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Delegating funds to the contract using the factory +```js +{ + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForDelegating(alice.address, + { + delegationContract: contract, + amount: 5000000000000000000000n, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Redelegating rewards using the Controller +Over time, as rewards accumulate, we may choose to redelegate them to the contract to maximize earnings. + +```js +{ + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForRedelegatingRewards( + alice.address, + alice.getNonceThenIncrement(), + { + delegationContract: contract, + }); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Redelegating rewards using the factory +```js +{ + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForRedelegatingRewards(alice.address, + { + delegationContract: contract, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Claiming rewards using the Controller +We can also claim our rewards when needed. + +```js +{ + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForClaimingRewards( + alice.address, + alice.getNonceThenIncrement(), + { + delegationContract: contract, + }); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Claiming rewards using the factory +```js +{ + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForClaimingRewards(alice.address, + { + delegationContract: contract, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Undelegating funds using the Controller +By **undelegating**, we signal the contract that we want to retrieve our staked funds. This process requires a **10-epoch unbonding period** before the funds become available. + +```js +{ + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForUndelegating( + alice.address, + alice.getNonceThenIncrement(), + { + delegationContract: contract, + amount: 1000000000000000000000n // 1000 EGLD + }); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Undelegating funds using the factory +```js +{ + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForUndelegating(alice.address, + { + delegationContract: contract, + amount: 1000000000000000000000n // 1000 EGLD + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Withdrawing funds using the Controller +After the `10-epoch unbonding period` is complete, we can proceed with withdrawing our staked funds using the controller. This final step allows us to regain access to the previously delegated funds. + +```js +{ + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForWithdrawing( + alice.address, + alice.getNonceThenIncrement(), + { + delegationContract: contract, + }); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Withdrawing funds using the factory +```js +{ + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForWithdrawing(alice.address, + { + delegationContract: contract, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +### Relayed transactions +We are currently on the `third iteration (V3)` of relayed transactions. V1 and V2 will soon be deactivated, so we will focus on V3. + +For V3, two new fields have been added to transactions: +- relayer +- relayerSignature + +Signing Process: +1. The relayer must be set before the sender signs the transaction. +2. Once the sender has signed, the relayer can also sign the transaction and broadcast it. + +**Important Consideration**: +Relayed V3 transactions require an additional `50,000` gas. +Let’s see how to create a relayed transaction: + +```js +{ + const walletsPath = path.join("src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + const bob = await Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = new Transaction({ + chainID: "D", + sender: alice.address, + receiver: bob, + relayer: carol.address, + gasLimit: 110_000n, + data: Buffer.from("hello"), + nonce: alice.getNonceThenIncrement() + }); + + // sender signs the transaction + transaction.signature = await alice.signTransaction(transaction); + + // relayer signs the transaction + transaction.RelayerSignature = await carol.signTransaction(transaction); + + // broadcast the transaction + const entrypoint = new DevnetEntrypoint(); + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Creating relayed transactions using controllers +We can create relayed transactions using any of the available controllers. +Each controller includes a relayer argument, which must be set if we want to create a relayed transaction. + +Let’s issue a fungible token using a relayed transaction: + +```js +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.creatTokenManagementController(); + + // create the issuer of the token + const walletsPath = path.join("src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // Carol will be our relayer, that means she is paying the gas for the transaction + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + guardian: carol.address, + }, + ); + + // relayer also signs the transaction + transaction.relayerSignature = await carol.signTransaction(transaction); + + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Creating relayed transactions using factories +Unlike controllers, `transaction factories` do not have a `relayer` argument. Instead, the **relayer must be set after creating the transaction**. +This approach is beneficial because the **transaction is not signed by the sender at the time of creation**, allowing flexibility in setting the relayer before signing. + +Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: + +```js +{ + // create the entrypoint and the token management factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.creatTokenManagementController(); + + // create the issuer of the token + const walletsPath = path.join("src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // carol will be our relayer, that means she is paying the gas for the transaction + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + const transaction = await factory.createTransactionForIssuingFungible( + alice.address, + { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }, + ); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // set the relayer + transaction.relayer = carol.address; + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // relayer also signs the transaction + transaction.relayerSignature = await carol.signTransaction(transaction); + + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +### Guarded transactions +Similar to relayers, transactions also have two additional fields: + +- guardian +- guardianSignature + +Each controller includes an argument for the guardian. The transaction can either: +1. Be sent to a service that signs it using the guardian’s account, or +2. Be signed by another account acting as a guardian. + +Let’s issue a token using a guarded account: + +#### Creating guarded transactions using controllers +We can create guarded transactions using any of the available controllers. + +Each controller method includes a guardian argument, which must be set if we want to create a guarded transaction. +Let’s issue a fungible token using a relayed transaction: + +```js +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.creatTokenManagementController(); + + // create the issuer of the token + const walletsPath = path.join("src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // carol will be our guardian + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + guardian: carol.address, + }, + ); + + // guardian also signs the transaction + transaction.guardianSignature = await carol.signTransaction(transaction); + + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Creating guarded transactions using factories +Unlike controllers, `transaction factories` do not have a `guardian` argument. Instead, the **guardian must be set after creating the transaction**. +This approach is beneficial because the transaction is **not signed by the sender at the time of creation**, allowing flexibility in setting the guardian before signing. + +Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: + +```js +{ + // create the entrypoint and the token management factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementController(); + + // create the issuer of the token + const walletsPath = path.join("src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // carol will be our guardian + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + const transaction = await factory.createTransactionForIssuingFungible( + alice.address, + { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }, + ); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // set the guardian + transaction.guardian = carol.address; + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // guardian also signs the transaction + transaction.guardianSignature = await carol.signTransaction(transaction); + + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +We can create guarded relayed transactions just like we did before. However, keep in mind: + +Only the sender can be guarded, the relayer cannot be guarded. + +Flow for Creating Guarded Relayed Transactions: +- Using Controllers: +1. Set both guardian and relayer fields. +2. The transaction must be signed by both the guardian and the relayer. +- Using Factories: + +1. Create the transaction. +2. Set both guardian and relayer fields. +3. First, the sender signs the transaction. +4. Then, the guardian signs. +5. Finally, the relayer signs before broadcasting. + +## Addresses + +Create an `Address` object from a bech32-encoded string: + +``` js +{ + // Create an Address object from a bech32-encoded string + const address = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + + console.log("Address (bech32-encoded):", address.toBech32()); + console.log("Public key (hex-encoded):", address.toHex()); + console.log("Public key (hex-encoded):", Buffer.from(address.getPublicKey()).toString("hex")); +} + +``` + +Here’s how you can create an address from a hex-encoded string using the MultiversX JavaScript SDK: +If the HRP (human-readable part) is not provided, the SDK will use the default one ("erd"). + +``` js +{ + // Create an address from a hex-encoded string with a specified HRP + const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "erd"); + + console.log("Address (bech32-encoded):", address.toBech32()); + console.log("Public key (hex-encoded):", address.toHex()); +} +``` + +#### Create an address from a raw public key + +``` js +{ + const pubkey = Buffer.from("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "hex"); + const addressFromPubkey = new Address(pubkey, "erd"); +} +``` + +#### Using an AddressFactory to create addresses +AddressFactory allows creating addresses with a custom HRP, ensuring consistency across your application. + +``` js +{ + const factory = new AddressFactory("erd"); + + const address1 = factory.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const address2 = factory.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); + const address3 = factory.fromPublicKey(Buffer.from("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "hex")); +} +``` + +#### Getting the shard of an address +``` js + +const addressComputer = new AddressComputer(); +console.log("Shard:", addressComputer.getShardOfAddress(address)); +``` + +Checking if an address is a smart contract +``` js + +const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm"); +console.log("Is contract address:", contractAddress.isSmartContract()); +``` + +### Changing the default hrp +The **LibraryConfig** class manages the default **HRP** (human-readable part) for addresses, which is set to `"erd"` by default. +You can change the HRP when creating an address or modify it globally in **LibraryConfig**, affecting all newly created addresses. +``` js + +console.log(LibraryConfig.defaultAddressHrp); +const defaultAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); +console.log(defaultAddress.toBech32()); + +LibraryConfig.defaultAddressHrp = "test"; +const testAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); +console.log(testAddress.toBech32()); + +// Reset HRP back to "erd" to avoid affecting other parts of the application. +LibraryConfig.defaultAddressHrp = "erd"; +``` + +## Wallets + +#### Generating a mnemonic +Mnemonic generation is based on [bip39](https://www.npmjs.com/package/bip39) and can be achieved as follows: + +``` js + +const mnemonic = Mnemonic.generate(); +const words = mnemonic.getWords(); +console.log({ words }); +``` + +#### Saving the mnemonic to a keystore file +The mnemonic can be saved to a keystore file: + +``` js +{ + const mnemonic = Mnemonic.generate(); + + // saves the mnemonic to a keystore file with kind=mnemonic + const wallet = UserWallet.fromMnemonic({ mnemonic: mnemonic.getText(), password: "password" }); + + const filePath = path.join("src", "testdata", "testwallets", "walletWithMnemonic.json"); + wallet.save(filePath); +} +``` + +#### Deriving secret keys from a mnemonic +Given a mnemonic, we can derive keypairs: + +``` js +{ + const mnemonic = Mnemonic.generate(); + + const secretKey = mnemonic.deriveKey(0); + const publicKey = secretKey.generatePublicKey(); + + console.log("Secret key: ", secretKey.hex()); + console.log("Public key: ", publicKey.hex()); +} +``` + +#### Saving a secret key to a keystore file +The secret key can also be saved to a keystore file: + +``` js +{ + const mnemonic = Mnemonic.generate(); + const secretKey = mnemonic.deriveKey(); + + const wallet = UserWallet.fromSecretKey({ secretKey: secretKey, password: "password" }); + + const filePath = path.join("src", "testdata", "testwallets", "walletWithSecretKey.json"); + wallet.save(filePath); +} +``` + +#### Saving a secret key to a PEM file +We can save a secret key to a pem file. *This is not recommended as it is not secure, but it's very convenient for testing purposes.* + +``` js +{ + const mnemonic = Mnemonic.generate(); + + // by default, derives using the index = 0 + const secretKey = mnemonic.deriveKey(); + const publicKey = secretKey.generatePublicKey(); + + const label = publicKey.toAddress().toBech32(); + const pem = new UserPem(label, secretKey); + + const filePath = path.join("src", "testdata", "testwallets", "wallet.pem"); + pem.save(filePath); +} +``` + +#### Generating a KeyPair +A `KeyPair` is a wrapper over a secret key and a public key. We can create a keypair and use it for signing or verifying. + +``` js +{ + const keypair = KeyPair.generate(); + + // by default, derives using the index = 0 + const secretKey = keypair.getSecretKey(); + const publicKey = keypair.getPublicKey(); +} +``` + +#### Loading a wallet from keystore mnemonic file +Load a keystore that holds an encrypted mnemonic (and perform wallet derivation at the same time): + +``` js +{ + const filePath = path.join("src", "testdata", "testwallets", "walletWithMnemonic.json"); + + // loads the mnemonic and derives the a secret key; default index = 0 + let secretKey = UserWallet.loadSecretKey(filePath, "password"); + let address = secretKey.generatePublicKey().toAddress('erd'); + + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); + + // derive secret key with index = 7 + secretKey = UserWallet.loadSecretKey(path, "password", 7); + address = secretKey.generatePublicKey().toAddress(); + + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); +} +``` + +#### Loading a wallet from a keystore secret key file + +``` js +{ + const filePath = path.join("src", "testdata", "testwallets", "walletWithSecretKey.json"); + + let secretKey = UserWallet.loadSecretKey(filePath, "password"); + let address = secretKey.generatePublicKey().toAddress('erd'); + + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); +} +``` + +#### Loading a wallet from a PEM file + +``` js +{ + const filePath = path.join("src", "testdata", "testwallets", "wallet.pem"); + + let pem = UserPem.fromFile(filePath); + + console.log("Secret key: ", pem.secretKey.hex()); + console.log("Public key: ", pem.publicKey.hex()); +} +``` + +## Signing objects + +Signing is done using an account's secret key. To simplify this process, we provide wrappers like [Account](#creating-accounts), which streamline signing operations. +First, we'll explore how to sign using an Account, followed by signing directly with a secret key. + +#### Signing a Transaction using an Account +We are going to assume we have an account at this point. If you don't, feel free to check out the [creating an account](#creating-accounts) section. +```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + chainID: "D", + sender: alice.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + gasLimit: 50000n, + nonce: 90n + }); + + transaction.signature = await alice.signTransaction(transaction); + console.log(transaction.toPlainObject()); +} +``` + +#### Signing a Transaction using a SecretKey +```js +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = UserSecretKey.fromString(secretKeyHex); + const publickKey = secretKey.generatePublicKey(); + + const transaction = new Transaction({ + nonce: 90n, + sender: publickKey.toAddress(), + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D" + }); + + // serialize the transaction + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForSigning(transaction); + + // apply the signature on the transaction + transaction.signature = await secretKey.sign(serializedTransaction); + + console.log(transaction.toPlainObject()); +} +``` + +#### Signing a Transaction by hash +```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + nonce: 90n, + sender: publickKey.toAddress(), + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D" + }); + + const transactionComputer = new TransactionComputer(); + + // sets the least significant bit of the options field to `1` + transactionComputer.applyOptionsForHashSigning(transaction); + + // compute a keccak256 hash for signing + const hash = transactionComputer.computeHashForSigning(transaction); + + // sign and apply the signature on the transaction + transaction.signature = await alice.signTransaction(hash); + + console.log(transaction.toPlainObject()); +} +``` + +#### Signing a Message using an Account: +```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: alice.address + }); + + message.signature = await alice.signMessage(message); +} +``` + +#### Signing a Message using an SecretKey: +```js +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = UserSecretKey.fromString(secretKeyHex); + const publicKey = secretKey.generatePublicKey(); + + const messageComputer = new MessageComputer(); + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: publicKey.toAddress() + }); + // serialized the message + const serialized = messageComputer.computeBytesForSigning(message); + + message.signature = await secretKey.sign(serialized); +} +``` + +## Verifying signatures + +Signature verification is performed using an account’s public key. +To simplify this process, we provide wrappers over public keys that make verification easier and more convenient. + +#### Verifying Transaction signature using a UserVerifier + +```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + nonce: 90n, + sender: account.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D" + }); + + // sign and apply the signature on the transaction + transaction.signature = await account.sign(transaction); + + // instantiating a user verifier; basically gets the public key + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const aliceVerifier = UserVerifier.fromAddress(alice); + + // serialize the transaction for verification + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); + + // verify the signature + const isSignedByAlice = aliceVerifier.verify(serializedTransaction, transaction.signature); + + + console.log("Transaction is signed by Alice: ", isSignedByAlice); +} +``` + +#### Verifying Message signature using a UserVerifier + +```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); + + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: account.address + }); + + // sign and apply the signature on the message + message.signature = await account.sign(message); + + // instantiating a user verifier; basically gets the public key + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const aliceVerifier = UserVerifier.fromAddress(alice); + + // serialize the message for verification + const messageComputer = new MessageComputer(); + const serializedMessage = messageComputer.computeBytesForVerifying(message); + + // verify the signature + const isSignedByAlice = aliceVerifier.verify(serializedMessage, message.signature); + + console.log("Message is signed by Alice: ", isSignedByAlice); +} +``` + +#### Verifying a signature using a public key +```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + nonce: 90n, + sender: account.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D" + }); + + // sign and apply the signature on the transaction + transaction.signature = await account.sign(transaction); + + // instantiating a public key + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const publicKey = new UserPublicKey(alice.getPublicKey()); + + // serialize the transaction for verification + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); + + // verify the signature + const isSignedByAlice = publicKey.verify(serializedTransaction, transaction.signature); + console.log("Transaction is signed by Alice: ", isSignedByAlice); +} +``` + +#### Sending messages over boundaries +Signed Message objects are typically sent to a remote party (e.g., a service), which can then verify the signature. +To prepare a message for transmission, you can use the `MessageComputer.packMessage()` utility method. + +```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); + + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: account.address + }); + + // sign and apply the signature on the message + message.signature = await account.sign(message); + + const messageComputer = new MessageComputer(); + const packedMessage = messageComputer.packMessage(message); + + console.log("Packed message", packedMessage); +} +``` + +Then, on the receiving side, you can use [`MessageComputer.unpackMessage()`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/MessageComputer.html#unpackMessage) to reconstruct the message, prior verification: + +```js +{ + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + + const messageComputer = new MessageComputer(); + + // restore message + const message = messageComputer.unpackMessage(packedMessage); + + // verify the signature + const publicKey = new UserPublicKey(alice.getPublicKey()); + const isSignedByAlice = publicKey.verify(messageComputer.computeBytesForVerifying(message), message.signature); + + console.log("Transaction is signed by Alice: ", isSignedByAlice); +} +``` diff --git a/cookbook/delegation.js b/cookbook/delegation.js new file mode 100644 index 00000000..37b92de4 --- /dev/null +++ b/cookbook/delegation.js @@ -0,0 +1,383 @@ +import { Account, Address, DevnetEntrypoint, TokenManagementTransactionsOutcomeParser } from "@multiversx/sdk-core"; // md-ignore +import path from 'path'; // md-ignore +// ### Delegation management + +// To learn more about staking providers and delegation, please refer to the official [documentation](https://docs.multiversx.com/validators/delegation-manager/#introducing-staking-providers). +// In this section, we'll cover how to: +// - Create a new delegation contract +// - Retrieve the contract address +// - Delegate funds to the contract +// - Redelegate rewards +// - Claim rewards +// - Undelegate and withdraw funds + +// These operations can be performed using both the controller and the **factory**. For a complete list of supported methods, please refer to the autogenerated documentation: +// - [DelegationController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationController.html) +// - [DelegationTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationTransactionsFactory.html) + +// #### Creating a New Delegation Contract Using the Controller +// ```js +{ + // create the entrypoint and the delegation controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForNewDelegationContract( + alice.address, + alice.getNonceThenIncrement(), + { + totalDelegationCap: 0, + serviceFee: 10n, + amount: 1250000000000000000000n, + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction completion, extract delegation contract's address // md-as-comment + const outcome = await controller.awaitCompletedCreateNewDelegationContract(txHash); + + const contractAddress = outcome[0].contractAddress; +} +// ``` + +// #### Creating a new delegation contract using the factory +// ```js +{ + // create the entrypoint and the delegation factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + + const transaction = await factory.createTransactionForNewDelegationContract(alice.address, + { + totalDelegationCap: 0, + serviceFee: 10n, + amount: 1250000000000000000000n, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // waits until the transaction is processed and fetches it from the network // md-as-comment + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the contract address // md-as-comment + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueFungible(transactionOnNetwork); + const contractAddress = outcome[0].contractAddress; +} +// ``` + +// #### Delegating funds to the contract using the Controller +// We can send funds to a delegation contract to earn rewards. + +// ```js +{ + // create the entrypoint and the delegation controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await controller.createTransactionForDelegating( + alice.address, + alice.getNonceThenIncrement(), + { + delegationContract: contract, + amount: 5000000000000000000000n, + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Delegating funds to the contract using the factory +// ```js +{ + // create the entrypoint and the delegation factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForDelegating(alice.address, + { + delegationContract: contract, + amount: 5000000000000000000000n, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Redelegating rewards using the Controller +// Over time, as rewards accumulate, we may choose to redelegate them to the contract to maximize earnings. + +// ```js +{ + // create the entrypoint and the delegation controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForRedelegatingRewards( + alice.address, + alice.getNonceThenIncrement(), + { + delegationContract: contract, + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Redelegating rewards using the factory +// ```js +{ + // create the entrypoint and the delegation factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForRedelegatingRewards(alice.address, + { + delegationContract: contract, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Claiming rewards using the Controller +// We can also claim our rewards when needed. + +// ```js +{ + // create the entrypoint and the delegation controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForClaimingRewards( + alice.address, + alice.getNonceThenIncrement(), + { + delegationContract: contract, + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Claiming rewards using the factory +// ```js +{ + // create the entrypoint and the delegation factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForClaimingRewards(alice.address, + { + delegationContract: contract, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Undelegating funds using the Controller +// By **undelegating**, we signal the contract that we want to retrieve our staked funds. This process requires a **10-epoch unbonding period** before the funds become available. + +// ```js +{ + // create the entrypoint and the delegation controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForUndelegating( + alice.address, + alice.getNonceThenIncrement(), + { + delegationContract: contract, + amount: 1000000000000000000000n // 1000 EGLD // md-as-comment + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Undelegating funds using the factory +// ```js +{ + // create the entrypoint and the delegation factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForUndelegating(alice.address, + { + delegationContract: contract, + amount: 1000000000000000000000n // 1000 EGLD // md-as-comment + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Withdrawing funds using the Controller +// After the `10-epoch unbonding period` is complete, we can proceed with withdrawing our staked funds using the controller. This final step allows us to regain access to the previously delegated funds. + +// ```js +{ + // create the entrypoint and the delegation controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForWithdrawing( + alice.address, + alice.getNonceThenIncrement(), + { + delegationContract: contract, + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Withdrawing funds using the factory +// ```js +{ + // create the entrypoint and the delegation factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForWithdrawing(alice.address, + { + delegationContract: contract, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` \ No newline at end of file diff --git a/cookbook/entrypoints.js b/cookbook/entrypoints.js new file mode 100644 index 00000000..5ab8539e --- /dev/null +++ b/cookbook/entrypoints.js @@ -0,0 +1,34 @@ +import { DevnetEntrypoint } from "@multiversx/sdk-core"; // md-ignore +// ## Overview + +// This guide walks you through handling common tasks using the MultiversX Javascript SDK (v14, latest stable version). + +// ## Creating an Entrypoint + +// An Entrypoint represents a network client that simplifies access to the most common operations. +// There is a dedicated entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint`, `LocalnetEntrypoint`. + +// For example, to create a Devnet entrypoint you have the following command: + +// ```js +const entrypoint = new DevnetEntrypoint(); +// ``` + +// #### Using a Custom API +// If you'd like to connect to a third-party API, you can specify the url parameter: + +// ```js +const apiEntrypoint = new DevnetEntrypoint({ url: 'https://custom-multiversx-devnet-api.com' }); +// ``` + +// #### Using a Proxy + +// By default, the DevnetEntrypoint uses the standard API. However, you can create a custom entrypoint that interacts with a proxy by specifying the kind parameter: + + +// ```js +const customEntrypoint = new DevnetEntrypoint({ + url: 'https://devnet-gateway.multiversx.com', + kind: 'proxy' +}); +// ``` diff --git a/cookbook/generate.py b/cookbook/generate.py new file mode 100644 index 00000000..d6ab3e52 --- /dev/null +++ b/cookbook/generate.py @@ -0,0 +1,129 @@ +import re +from pathlib import Path +from typing import Dict, List + +current_dir = Path(__file__).parent.absolute() + +input_files = [ + current_dir / "entrypoints.js", + current_dir / "account.js", + current_dir / "networkProviders.js", + current_dir / "transactions.js", + current_dir / "smartContracts.js", + current_dir / "tokens.js", + current_dir / "accountManagement.js", + current_dir / "delegation.js", + current_dir / "relayed.js", + current_dir / "guarded.js", + current_dir / "addresses.js", + current_dir / "wallets.js", + current_dir / "signingObjects.js", + current_dir / "verifySignatures.js" +] + +MARKER_INSERT = "md-insert:" +DIRECTIVE_PREFIX = "// md-" +DIRECTIVE_IGNORE = "// md-ignore" +DIRECTIVE_UNINDENT = "// md-unindent" +DIRECTIVE_AS_COMMENT = "// md-as-comment" +DIRECTIVE_INSERT = f"// {MARKER_INSERT}" + +API_URL = "https://multiversx.github.io/mx-sdk-js-core" +API_DEFAIULT_VERSION = "v13" +DOCS_URL = "https://docs.multiversx.com" + +notes: Dict[str, str] = { + "transactionLegacyVsNext": """:::note +Since `sdk-core v13`, the `class:Transaction` class exhibits its state as public read-write properties. For example, you can access and set the `nonce` property, instead of using `getNonce` and `setNonce`. +:::""", + + "forSimplicityWeUseUserSigner": f""":::important +For the sake of simplicity, in this section we'll use a `UserSigner` object to sign the transaction. +In real-world dApps, transactions are signed by end-users using their wallet, through a [signing provider]({DOCS_URL}/sdk-and-tools/sdk-js/sdk-js-signing-providers). +::: +""", + + "mixedTypedValuesAndNativeValues": """:::tip +When creating transactions using `class:SmartContractController` or `class:SmartContractTransactionsFactory`, even if the ABI is available and provided, +you can still use `class:TypedValue` objects as arguments for deployments and interactions. + +Even further, you can use a mix of `class:TypedValue` objects and plain JavaScript values and objects. For example: + +```js +let args = [new U32Value(42), "hello", { foo: "bar" }, new TokenIdentifierValue("TEST-abcdef")]; +``` + +:::""", +} + + +def main(): + output_file = current_dir / "cookbook.md" + output_sections: List[str] = [] + + for input_file in input_files: + lines = render_file(input_file) + section = "\n".join(lines).strip() + output_sections.append(section) + + output_text = "\n\n".join(output_sections) + "\n" + output_text = render_api_links(output_text) + output_text = remove_docs_root_url(output_text) + output_file.write_text(output_text) + + +def render_file(input_file: Path) -> List[str]: + input_text = input_file.read_text() + input_lines = input_text.splitlines() + output_lines: List[str] = [] + + for line in input_lines: + should_ignore = DIRECTIVE_IGNORE in line + should_unindent = DIRECTIVE_UNINDENT in line + is_comment = line.startswith("//") + should_keep_as_comment = DIRECTIVE_AS_COMMENT in line + should_insert = DIRECTIVE_INSERT in line + + if should_ignore: + continue + + if should_unindent: + line = line.lstrip() + + if is_comment and not should_keep_as_comment: + line = line[2:].strip() + + line = line.replace(DIRECTIVE_UNINDENT, "") + line = line.replace(DIRECTIVE_AS_COMMENT, "") + + if should_insert: + box_name = line.replace(MARKER_INSERT, "").strip() + box_content = notes[box_name] + line = box_content + + line = line.rstrip() + output_lines.append(line) + + return output_lines + + +def render_api_links(input: str) -> str: + matches_func = re.findall(r"`func:(\w+\.\w+)\(\)`", input) + matches_class = re.findall(r"`class:(\w+)`", input) + + for match in matches_func: + [class_name, method] = match.split(".") + input = input.replace(f"`func:{match}()`", f"[`{match}()`]({API_URL}/{API_DEFAIULT_VERSION}/classes/{class_name}.html#{method})") + + for match in matches_class: + input = input.replace(f"`class:{match}`", f"[`{match}`]({API_URL}/{API_DEFAIULT_VERSION}/classes/{match}.html)") + + return input + + +def remove_docs_root_url(input: str) -> str: + return input.replace(DOCS_URL, "") + + +if __name__ == "__main__": + main() diff --git a/cookbook/guarded.js b/cookbook/guarded.js new file mode 100644 index 00000000..e70723d7 --- /dev/null +++ b/cookbook/guarded.js @@ -0,0 +1,130 @@ +import { Account, DevnetEntrypoint } from "@multiversx/sdk-core"; // md-ignore +import path from 'path'; // md-ignore +// ### Guarded transactions +// Similar to relayers, transactions also have two additional fields: + +// - guardian +// - guardianSignature + +// Each controller includes an argument for the guardian. The transaction can either: +// 1. Be sent to a service that signs it using the guardian’s account, or +// 2. Be signed by another account acting as a guardian. + +// Let’s issue a token using a guarded account: + +// #### Creating guarded transactions using controllers +// We can create guarded transactions using any of the available controllers. + +// Each controller method includes a guardian argument, which must be set if we want to create a guarded transaction. +// Let’s issue a fungible token using a relayed transaction: + +// ```js +{ + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.creatTokenManagementController(); + + // create the issuer of the token // md-as-comment + const walletsPath = path.join("src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // carol will be our guardian // md-as-comment + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + guardian: carol.address, + }, + ); + + // guardian also signs the transaction // md-as-comment + transaction.guardianSignature = await carol.signTransaction(transaction); + + // broadcast the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Creating guarded transactions using factories +// Unlike controllers, `transaction factories` do not have a `guardian` argument. Instead, the **guardian must be set after creating the transaction**. +// This approach is beneficial because the transaction is **not signed by the sender at the time of creation**, allowing flexibility in setting the guardian before signing. + +// Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: + +// ```js +{ + // create the entrypoint and the token management factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementController(); + + // create the issuer of the token // md-as-comment + const walletsPath = path.join("src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // carol will be our guardian // md-as-comment + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + const transaction = await factory.createTransactionForIssuingFungible( + alice.address, + { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }, + ); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // set the guardian // md-as-comment + transaction.guardian = carol.address; + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // guardian also signs the transaction // md-as-comment + transaction.guardianSignature = await carol.signTransaction(transaction); + + // broadcast the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// We can create guarded relayed transactions just like we did before. However, keep in mind: + +// Only the sender can be guarded, the relayer cannot be guarded. + +// Flow for Creating Guarded Relayed Transactions: +// - Using Controllers: +// 1. Set both guardian and relayer fields. +// 2. The transaction must be signed by both the guardian and the relayer. +// - Using Factories: + +// 1. Create the transaction. +// 2. Set both guardian and relayer fields. +// 3. First, the sender signs the transaction. +// 4. Then, the guardian signs. +// 5. Finally, the relayer signs before broadcasting. \ No newline at end of file diff --git a/cookbook/networkProviders.js b/cookbook/networkProviders.js new file mode 100644 index 00000000..1f18f891 --- /dev/null +++ b/cookbook/networkProviders.js @@ -0,0 +1,447 @@ +import { Address, ApiNetworkProvider, DevnetEntrypoint, ProxyNetworkProvider, Token, Transaction } from "@multiversx/sdk-core"; // md-ignore +// ## Calling the Faucet + +// This functionality is not yet available through the entrypoint, but we recommend using the faucet available within the Web Wallet. For more details about the faucet [see this](https://docs.multiversx.com/wallet/web-wallet/#testnet-and-devnet-faucet). + +// - [Testnet Wallet](https://testnet-wallet.multiversx.com/). +// - [Devnet Wallet](https://devnet-wallet.multiversx.com/). + +// ### Interacting with the network + +// The entrypoint exposes a few ways to directly interact with the network, such as: + +// - `recallAccountNonce(address: Address): Promise;` +// - `sendTransactions(transactions: Transaction[]): Promise<[number, string[]]>;` +// - `sendTransaction(transaction: Transaction): Promise;` +// - `getTransaction(txHash: string): Promise;` +// - `awaitCompletedTransaction(txHash: string): Promise;` + +// Some other methods are exposed through a so called **network provider**. + +// - **ApiNetworkProvider**: Interacts with the API, which is a layer over the proxy. It fetches data from the network and `Elastic Search`. +// - **ProxyNetworkProvider**: Interacts directly with the proxy of an observing squad. + +// To get the underlying network provider from our entrypoint, we can do as follows: + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); +} +// ``` + +// ### Creating a network provider +// When manually instantiating a network provider, you can provide a configuration to specify the client name and set custom request options. + +// ```js +{ + // Create a configuration object + const config = { + clientName: "hello-multiversx", + requestsOptions: { + timeout: 1000, // Timeout in milliseconds // md-as-comment + auth: { + username: "user", + password: "password" + } + } + }; + + // Instantiate the network provider with the config + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com", config); +} +// ``` + +// A full list of available methods for `ApiNetworkProvider` can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/classes/ApiNetworkProvider.html). + +// Both `ApiNetworkProvider` and `ProxyNetworkProvider` implement a common interface, which can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/interfaces/INetworkProvider.html). This allows them to be used interchangeably. + +// The classes returned by the API expose the most commonly used fields directly for convenience. However, each object also contains a `raw` field that stores the original API response, allowing access to additional fields if needed. + + +// ## Fetching data from the network + +// ### Fetching the network config + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); + + const networkConfig = entrypoint.getNetworkConfig(); +} +// ``` + +// ### Fetching the network status + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); + + const metaNetworkStatus = entrypoint.getNetworkStatus(); // fetches status from metachain // md-as-comment + const networkStatus = entrypoint.getNetworkStatus(1); // fetches status from shard one // md-as-comment +} +// ``` + +// ### Fetching a Block from the Network +// To fetch a block, we first instantiate the required arguments and use its hash. The API only supports fetching blocks by hash, whereas the **PROXY** allows fetching blocks by either hash or nonce. + +// When using the **PROXY**, keep in mind that the shard must also be specified in the arguments. + +// #### Fetching a block using the **API** +// ```js +{ + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); + const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; + const block = await api.getBlock(blockHash); +} +// ``` + +// Additionally, we can fetch the latest block from the network: + +// ```js +{ + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); + const latestBlock = await api.getLatestBlock(); +} +// ``` + +// #### Fetching a block using the **PROXY** + +// When using the proxy, we have to provide the shard, as well. +// ```js +{ + const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); + const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; + const block = proxy.getBlock({ blockHash, shard: 1 }); +} +// ``` + +// We can also fetch the latest block from the network. +// By default, the shard will be the metachain, but we can specify a different shard if needed. + +// ```js +{ + const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); + const latestBlock = proxy.getLatestBlock(); +} +// ``` + +// ### Fetching an Account +// To fetch an account, we need its address. Once we have the address, we create an `Address` object and pass it as an argument to the method. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccount(alice); +} +// ``` + +// ### Fetching an Account's Storage +// We can also fetch an account's storage, allowing us to retrieve all key-value pairs saved for that account. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccountStorage(alice); +} +// ``` + +// If we only want to fetch a specific key, we can do so as follows: + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccountStorageEntry(alice, "testKey"); +} +// ``` + +// ### Waiting for an Account to Meet a Condition +// There are times when we need to wait for a specific condition to be met before proceeding with an action. +// For example, let's say we want to send 7 EGLD from Alice to Bob, but this can only happen once Alice's balance reaches at least 7 EGLD. +// This approach is useful in scenarios where you're waiting for external funds to be sent to Alice, enabling her to transfer the required amount to another recipient. + +// To implement this, we need to define the condition to check each time the account is fetched from the network. We create a function that takes an `AccountOnNetwork` object as an argument and returns a `bool`. +// Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const condition = (account) => { + return account.balance >= 7000000000000000000; // 7 EGLD // md-as-comment + }; + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.awaitAccountOnCondition(alice, condition); +} +// ``` + +// ### Sending and Simulating Transactions +// To execute transactions, we use the network providers to broadcast them to the network. Keep in mind that for transactions to be processed, they must be signed. + +// #### Sending a Transaction + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = new Transaction({ + sender: alice, + receiver: bob, + gasLimit: 50000n, + chainID: "D", + }); + + // set the correct nonce and sign the transaction ... + + const transactionHash = await api.sendTransaction(transaction); +} +// ``` + +// #### Sending multiple transactions +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const firstTransaction = new Transaction({ + sender: alice, + receiver: bob, + gasLimit: 50000n, + chainID: "D", + nonce: 2 + }); + + const secondTransaction = new Transaction({ + sender: bob, + receiver: alice, + gasLimit: 50000n, + chainID: "D", + nonce: 1, + }); + + const thirdTransaction = new Transaction({ + sender: alice, + receiver: alice, + gasLimit: 60000n, + chainID: "D", + nonce: 3, + data: new Uint8Array(Buffer.from("hello")) + }); + + // set the correct nonce and sign the transaction ... + + const [numOfSentTxs, hashes] = await api.sendTransactions([firstTransaction, secondTransaction, thirdTransaction]); +} +// ``` + +// #### Simulating transactions +// A transaction can be simulated before being sent for processing by the network. This is primarily used for smart contract calls, allowing you to preview the results produced by the smart contract. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); + + const transaction = new Transaction({ + sender: alice, + receiver: contract, + gasLimit: 5000000n, + chainID: "D", + data: new Uint8Array(Buffer.from("add@07")) + }); + + const transactionOnNetwork = await api.simulateTransaction(transaction); +} +// ``` + +// #### Estimating the gas cost of a transaction +// Before sending a transaction to the network for processing, you can retrieve the estimated gas limit required for the transaction to be executed. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); + + const nonce = await entrypoint.recallAccountNonce(alice); + + const transaction = new Transaction({ + sender: alice, + receiver: contract, + gasLimit: 5000000, + chainID: "D", + data: new Uint8Array(Buffer.from("add@07")), + nonce: nonce + }); + + const transactionCostResponse = await api.estimateTransactionCost(transaction); +} +// ``` + +// ### Waiting for transaction completion +// After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); +} +// ``` + +// ### Waiting for a Transaction to Satisfy a Condition +// Similar to accounts, we can wait until a transaction meets a specific condition. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const condition = (txOnNetwork) => !txOnNetwork.status.isSuccessful(); + + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionOnCondition(txHash, condition); +} +// ``` + +// ### Waiting for transaction completion +// After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); +} +// ``` + +// ### Fetching Transactions from the Network +// After sending a transaction, we can fetch it from the network using the transaction hash, which we receive after broadcasting the transaction. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.getTransaction(txHash); +} +// ``` + +// ### Fetching a token from an account +// We can fetch a specific token (ESDT, MetaESDT, SFT, NFT) from an account by providing the account's address and the token identifier. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + let token = new Token({ identifier: "TEST-ff155e" }); // ESDT // md-as-comment + let tokenOnNetwork = await api.getTokenOfAccount(alice, token); + + + token = new Token({ identifier: "NFT-987654", nonce: 11n }); // NFT // md-as-comment + tokenOnNetwork = await api.getTokenOfAccount(alice, token); +} +// ``` + +// ### Fetching all fungible tokens of an account +// Fetches all fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const fungibleTokens = await api.getFungibleTokensOfAccount(alice); +} +// ``` + +// ### Fetching all non-fungible tokens of an account +// Fetches all non-fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const nfts = await api.getNonFungibleTokensOfAccount(alice); +} +// ``` + +// ### Fetching token metadata +// If we want to fetch the metadata of a token (e.g., owner, decimals, etc.), we can use the following methods: + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + // used for ESDT // md-as-comment + const fungibleTokenDefinition = await api.getDefinitionOfFungibleToken("TEST-ff155e"); + + // used for METAESDT, SFT, NFT // md-as-comment + const nonFungibleTokenDefinition = await api.getDefinitionOfTokenCollection("NFTEST-ec88b8"); +} +// ``` + +// ### Querying Smart Contracts +// Smart contract queries, or view functions, are endpoints that only read data from the contract. To send a query to the observer nodes, we can proceed as follows: + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const query = new SmartContractQuery({ + contract: Address.newFromBech32("erd1qqqqqqqqqqqqqpgqqy34h7he2ya6qcagqre7ur7cc65vt0mxrc8qnudkr4"), + function: "getSum", + arguments: [], + }); + const response = await api.queryContract(query); +} +// ``` + +// ### Custom Api/Proxy calls +// The methods exposed by the `ApiNetworkProvider` or `ProxyNetworkProvider` are the most common and widely used. However, there may be times when custom API calls are needed. For these cases, we’ve created generic methods for both GET and POST requests. +// Let’s assume we want to retrieve all the transactions sent by Alice in which the `delegate` function was called. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const url = `transactions/${alice.toBech32()}?function=delegate`; + + const response = await api.doGetGeneric(url); +} +// ``` \ No newline at end of file diff --git a/cookbook/package-lock.json b/cookbook/package-lock.json new file mode 100644 index 00000000..8d2d72d0 --- /dev/null +++ b/cookbook/package-lock.json @@ -0,0 +1,4620 @@ +{ + "name": "cookbook", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cookbook", + "dependencies": { + "@multiversx/sdk-core": "14.0.0", + "@multiversx/sdk-dapp": "2.40.11", + "axios": "1.7.7", + "chai": "5.1.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", + "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ethereumjs/common": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-3.2.0.tgz", + "integrity": "sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==", + "license": "MIT", + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "crc-32": "^1.2.0" + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "license": "MPL-2.0", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/tx": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-4.2.0.tgz", + "integrity": "sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==", + "license": "MPL-2.0", + "dependencies": { + "@ethereumjs/common": "^3.2.0", + "@ethereumjs/rlp": "^4.0.1", + "@ethereumjs/util": "^8.1.0", + "ethereum-cryptography": "^2.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "license": "MPL-2.0", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz", + "integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz", + "integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz", + "integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==", + "hasInstallScript": true, + "license": "(CC-BY-4.0 AND MIT)", + "optional": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", + "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", + "license": "MIT", + "optional": true, + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@ledgerhq/devices": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.0.3.tgz", + "integrity": "sha512-Q7/vqkGELSBuwFafFoTqlHIRyOjw8JqbSgiQwe2R38xN0RKtKIh+5E6UfMKyoExiq+SrQg0IC8P2LS+XdjOHLw==", + "license": "Apache-2.0", + "dependencies": { + "@ledgerhq/errors": "^6.12.6", + "@ledgerhq/logs": "^6.10.1", + "rxjs": "6", + "semver": "^7.3.5" + } + }, + "node_modules/@ledgerhq/errors": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.12.6.tgz", + "integrity": "sha512-D+r2B09vaRO06wfGoss+rNgwqWSoK0bCtsaJWzlD2hv1zxTtucqVtSztbRFypIqxWTCb3ix5Nh2dWHEJVTp2Xw==", + "license": "Apache-2.0" + }, + "node_modules/@ledgerhq/hw-transport": { + "version": "6.28.4", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.28.4.tgz", + "integrity": "sha512-fB2H92YQjidmae2GFCmOGPwkZWk0lvTu0tlLlzfiY0wRheAG+DEgjnqhdU8wmydkPLIj0WUjRgldtnJtg/a2iQ==", + "license": "Apache-2.0", + "dependencies": { + "@ledgerhq/devices": "^8.0.3", + "@ledgerhq/errors": "^6.12.6", + "events": "^3.3.0" + } + }, + "node_modules/@ledgerhq/hw-transport-web-ble": { + "version": "6.27.17", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-web-ble/-/hw-transport-web-ble-6.27.17.tgz", + "integrity": "sha512-OoKbImhgyi43F6PM2KAJfViW+oEmZ3M8Tq9XZ0X57oL6QCQUYv6FakEFFkZzNuNWFdtFKGPgNt6xygqIdNElEQ==", + "license": "Apache-2.0", + "dependencies": { + "@ledgerhq/devices": "^8.0.5", + "@ledgerhq/errors": "^6.13.0", + "@ledgerhq/hw-transport": "^6.28.6", + "@ledgerhq/logs": "^6.10.1", + "rxjs": "6" + } + }, + "node_modules/@ledgerhq/hw-transport-web-ble/node_modules/@ledgerhq/devices": { + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.4.4.tgz", + "integrity": "sha512-sz/ryhe/R687RHtevIE9RlKaV8kkKykUV4k29e7GAVwzHX1gqG+O75cu1NCJUHLbp3eABV5FdvZejqRUlLis9A==", + "license": "Apache-2.0", + "dependencies": { + "@ledgerhq/errors": "^6.19.1", + "@ledgerhq/logs": "^6.12.0", + "rxjs": "^7.8.1", + "semver": "^7.3.5" + } + }, + "node_modules/@ledgerhq/hw-transport-web-ble/node_modules/@ledgerhq/devices/node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@ledgerhq/hw-transport-web-ble/node_modules/@ledgerhq/errors": { + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.19.1.tgz", + "integrity": "sha512-75yK7Nnit/Gp7gdrJAz0ipp31CCgncRp+evWt6QawQEtQKYEDfGo10QywgrrBBixeRxwnMy1DP6g2oCWRf1bjw==", + "license": "Apache-2.0" + }, + "node_modules/@ledgerhq/hw-transport-web-ble/node_modules/@ledgerhq/hw-transport": { + "version": "6.31.4", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.31.4.tgz", + "integrity": "sha512-6c1ir/cXWJm5dCWdq55NPgCJ3UuKuuxRvf//Xs36Bq9BwkV2YaRQhZITAkads83l07NAdR16hkTWqqpwFMaI6A==", + "license": "Apache-2.0", + "dependencies": { + "@ledgerhq/devices": "^8.4.4", + "@ledgerhq/errors": "^6.19.1", + "@ledgerhq/logs": "^6.12.0", + "events": "^3.3.0" + } + }, + "node_modules/@ledgerhq/hw-transport-web-ble/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/@ledgerhq/hw-transport-webhid": { + "version": "6.27.15", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-webhid/-/hw-transport-webhid-6.27.15.tgz", + "integrity": "sha512-xhtFy/SNttvBna8t1ZiP74K6Lj8uDhp0W+Zjvwz4IS6tS9gWZbKUB7scbrNrJep6Q77Of1bzDXrswyaoxFZrUg==", + "license": "Apache-2.0", + "dependencies": { + "@ledgerhq/devices": "^8.0.3", + "@ledgerhq/errors": "^6.12.6", + "@ledgerhq/hw-transport": "^6.28.4", + "@ledgerhq/logs": "^6.10.1" + } + }, + "node_modules/@ledgerhq/hw-transport-webusb": { + "version": "6.27.15", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-webusb/-/hw-transport-webusb-6.27.15.tgz", + "integrity": "sha512-XRteQmJMFbMETOwwwJrXhEvBEdu1DpuDQNI04bzMQE9p81rLjwd6pIhfP/W1O94av7Iq4kaatNsNpxkitmp59w==", + "license": "Apache-2.0", + "dependencies": { + "@ledgerhq/devices": "^8.0.3", + "@ledgerhq/errors": "^6.12.6", + "@ledgerhq/hw-transport": "^6.28.4", + "@ledgerhq/logs": "^6.10.1" + } + }, + "node_modules/@ledgerhq/logs": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@ledgerhq/logs/-/logs-6.12.0.tgz", + "integrity": "sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA==", + "license": "Apache-2.0" + }, + "node_modules/@lifeomic/axios-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@lifeomic/axios-fetch/-/axios-fetch-3.0.1.tgz", + "integrity": "sha512-bwEgYXtGrn/F+yYqoUIAWBRzyqQ7yB1VL84gCq0uAk36GRoyoWyOzbE35VWeXlmkkoby91FnAwh4UhgayMM/og==", + "license": "MIT", + "dependencies": { + "@types/node-fetch": "^2.5.10" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@metamask/json-rpc-engine": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/@metamask/json-rpc-engine/-/json-rpc-engine-7.3.3.tgz", + "integrity": "sha512-dwZPq8wx9yV3IX2caLi9q9xZBw2XeIoYqdyihDDDpuHVCEiqadJLwqM3zy+uwf6F1QYQ65A8aOMQg1Uw7LMLNg==", + "license": "ISC", + "dependencies": { + "@metamask/rpc-errors": "^6.2.1", + "@metamask/safe-event-emitter": "^3.0.0", + "@metamask/utils": "^8.3.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/json-rpc-middleware-stream": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@metamask/json-rpc-middleware-stream/-/json-rpc-middleware-stream-6.0.2.tgz", + "integrity": "sha512-jtyx3PRfc1kqoLpYveIVQNwsxYKefc64/LCl9h9Da1m3nUKEvypbYuXSIwi237qvOjKmNHQKsDOZg6f4uBf62Q==", + "license": "ISC", + "dependencies": { + "@metamask/json-rpc-engine": "^7.3.2", + "@metamask/safe-event-emitter": "^3.0.0", + "@metamask/utils": "^8.3.0", + "readable-stream": "^3.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/object-multiplex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@metamask/object-multiplex/-/object-multiplex-2.1.0.tgz", + "integrity": "sha512-4vKIiv0DQxljcXwfpnbsXcfa5glMj5Zg9mqn4xpIWqkv6uJ2ma5/GtUfLFSxhlxnR8asRMv8dDmWya1Tc1sDFA==", + "license": "ISC", + "dependencies": { + "once": "^1.4.0", + "readable-stream": "^3.6.2" + }, + "engines": { + "node": "^16.20 || ^18.16 || >=20" + } + }, + "node_modules/@metamask/providers": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@metamask/providers/-/providers-16.0.0.tgz", + "integrity": "sha512-zqZpn7Tg+QuvhL9x1WYCdXjFclZrMR2LOU2a3kHVFms5pOfizzFApB492VFgHuv/5xKohSxDkhXpAZ/TNGmM4g==", + "license": "MIT", + "dependencies": { + "@metamask/json-rpc-engine": "^7.3.2", + "@metamask/json-rpc-middleware-stream": "^6.0.2", + "@metamask/object-multiplex": "^2.0.0", + "@metamask/rpc-errors": "^6.2.1", + "@metamask/safe-event-emitter": "^3.0.0", + "@metamask/utils": "^8.3.0", + "detect-browser": "^5.2.0", + "extension-port-stream": "^3.0.0", + "fast-deep-equal": "^3.1.3", + "is-stream": "^2.0.0", + "readable-stream": "^3.6.2", + "webextension-polyfill": "^0.10.0" + }, + "engines": { + "node": "^18.18 || >=20" + } + }, + "node_modules/@metamask/rpc-errors": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-6.4.0.tgz", + "integrity": "sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==", + "license": "MIT", + "dependencies": { + "@metamask/utils": "^9.0.0", + "fast-safe-stringify": "^2.0.6" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/rpc-errors/node_modules/@metamask/utils": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-9.3.0.tgz", + "integrity": "sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.1.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/rpc-errors/node_modules/@noble/hashes": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@metamask/rpc-errors/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@metamask/safe-event-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-3.1.2.tgz", + "integrity": "sha512-5yb2gMI1BDm0JybZezeoX/3XhPDOtTbcFvpTXM9kxsoZjPZFh4XciqRbpD6N86HYZqWDhEaKUDuOyR0sQHEjMA==", + "license": "ISC", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@metamask/superstruct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@metamask/superstruct/-/superstruct-3.1.0.tgz", + "integrity": "sha512-N08M56HdOgBfRKkrgCMZvQppkZGcArEop3kixNEtVbJKm6P9Cfg0YkI6X0s1g78sNrj2fWUwvJADdZuzJgFttA==", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.5.0.tgz", + "integrity": "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==", + "license": "ISC", + "dependencies": { + "@ethereumjs/tx": "^4.2.0", + "@metamask/superstruct": "^3.0.0", + "@noble/hashes": "^1.3.1", + "@scure/base": "^1.1.3", + "@types/debug": "^4.1.7", + "debug": "^4.3.4", + "pony-cause": "^2.1.10", + "semver": "^7.5.4", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@metamask/utils/node_modules/@noble/hashes": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", + "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@metamask/utils/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@multiversx/sdk-bls-wasm": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-bls-wasm/-/sdk-bls-wasm-0.3.5.tgz", + "integrity": "sha512-c0tIdQUnbBLSt6NYU+OpeGPYdL0+GV547HeHT8Xc0BKQ7Cj0v82QUoA2QRtWrR1G4MNZmLsIacZSsf6DrIS2Bw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/@multiversx/sdk-core": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-core/-/sdk-core-14.0.0.tgz", + "integrity": "sha512-cW1KViTPUv/VhoR0YrZT7zuTp2YOWmPxIay6yR0oOuOAWytOpUkwdDgAGKLsbonKq3yfehA+uK4yhazICJi2aA==", + "license": "MIT", + "dependencies": { + "@multiversx/sdk-transaction-decoder": "1.0.2", + "@noble/ed25519": "1.7.3", + "@noble/hashes": "1.3.0", + "bech32": "1.1.4", + "blake2b": "2.1.3", + "buffer": "6.0.3", + "ed25519-hd-key": "1.1.2", + "ed2curve": "0.3.0", + "json-bigint": "1.0.0", + "keccak": "3.0.2", + "scryptsy": "2.1.0", + "tweetnacl": "1.0.3", + "uuid": "8.3.2" + }, + "optionalDependencies": { + "@multiversx/sdk-bls-wasm": "0.3.5", + "axios": "^1.7.4", + "bip39": "3.1.0" + }, + "peerDependencies": { + "bignumber.js": "^9.0.1", + "protobufjs": "^7.2.6" + } + }, + "node_modules/@multiversx/sdk-dapp": { + "version": "2.40.11", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-dapp/-/sdk-dapp-2.40.11.tgz", + "integrity": "sha512-TBvZpvWDAWHoEw5ydDKQExVjSB54+jkzxWxZdttmLHKHK9W8Bk9dAbfD6CrN1AgKvbk3lz9JMCdh7mtqQYG63w==", + "license": "GPL-3.0-or-later", + "dependencies": { + "@lifeomic/axios-fetch": "3.0.1", + "@metamask/providers": "16.0.0", + "@multiversx/sdk-dapp-utils": "0.1.0", + "@multiversx/sdk-extension-provider": "3.0.0", + "@multiversx/sdk-hw-provider": "6.4.0", + "@multiversx/sdk-metamask-provider": "0.0.7", + "@multiversx/sdk-native-auth-client": "1.0.9", + "@multiversx/sdk-opera-provider": "1.0.0-alpha.1", + "@multiversx/sdk-passkey-provider": "1.0.9", + "@multiversx/sdk-wallet": "4.6.0", + "@multiversx/sdk-wallet-connect-provider": "4.1.4", + "@multiversx/sdk-web-wallet-cross-window-provider": "1.0.0", + "@multiversx/sdk-web-wallet-iframe-provider": "1.0.3", + "@multiversx/sdk-web-wallet-provider": "3.2.1", + "@multiversx/sdk-webview-provider": "1.0.0", + "@reduxjs/toolkit": "1.8.2", + "bignumber.js": "9.x", + "linkify-react": "4.0.2", + "linkifyjs": "4.0.2", + "lodash.debounce": "4.0.8", + "lodash.isequal": "4.5.0", + "lodash.omit": "4.5.0", + "lodash.throttle": "4.1.1", + "lodash.uniqby": "4.7.0", + "protobufjs": "7.2.6", + "qs": "6.10.3", + "react-idle-timer": "5.0.0", + "react-redux": "8.0.2", + "redux-persist": "6.0.0", + "reselect": "4.0.0", + "shx": "0.3.4", + "socket.io-client": "4.6.1" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@fortawesome/fontawesome-svg-core": "6.5.1", + "@fortawesome/free-solid-svg-icons": "6.5.1", + "@fortawesome/react-fontawesome": "0.2.0", + "classnames": "2.3.1", + "platform": "1.3.6", + "qrcode": "1.5.0", + "swr": "2.2.0" + }, + "peerDependencies": { + "@multiversx/sdk-core": ">= 12.18.0", + "@types/react": "^18.0.24", + "@types/react-dom": "^18.0.8", + "axios": ">=1.7.4", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/@multiversx/sdk-dapp-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-dapp-utils/-/sdk-dapp-utils-0.1.0.tgz", + "integrity": "sha512-EFvktZ/S1WQ1ie02nnKZHARC4r23JZWwoTFd5py1qi/Z/UoLHIzJ394HLjXFb6gBTsp4wnvNwIXBA/DNrd2Yeg==", + "license": "GPL-3.0-or-later", + "peerDependencies": { + "@multiversx/sdk-core": ">= 12.1.0" + } + }, + "node_modules/@multiversx/sdk-dapp/node_modules/protobufjs": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", + "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@multiversx/sdk-extension-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-extension-provider/-/sdk-extension-provider-3.0.0.tgz", + "integrity": "sha512-xNHLShzimYbMXqEJoiNyB4fNOXQUwSJCzt9FiOA4GdxucJnCgIM25mXtSj2I93cz+KD39QgRjEYep+li/lykOw==", + "license": "GPL-3.0-or-later", + "peerDependencies": { + "@multiversx/sdk-core": ">= 12.1.0" + } + }, + "node_modules/@multiversx/sdk-hw-provider": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-hw-provider/-/sdk-hw-provider-6.4.0.tgz", + "integrity": "sha512-o+iO64U7gi3oqQHIaCNWoOCarrOQyjXEq7kEsWg/HXJOANpc+lq4GiXPtBkN5mOwPoj/UlEk1HfxA+54mTCFNQ==", + "license": "MIT", + "dependencies": { + "@ledgerhq/devices": "8.0.3", + "@ledgerhq/errors": "6.12.6", + "@ledgerhq/hw-transport": "6.28.4", + "@ledgerhq/hw-transport-web-ble": "6.27.17", + "@ledgerhq/hw-transport-webhid": "6.27.15", + "@ledgerhq/hw-transport-webusb": "6.27.15", + "buffer": "6.0.3", + "platform": "1.3.6" + }, + "peerDependencies": { + "@multiversx/sdk-core": ">= 12.5.0" + } + }, + "node_modules/@multiversx/sdk-metamask-provider": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-metamask-provider/-/sdk-metamask-provider-0.0.7.tgz", + "integrity": "sha512-eqA1z/QIflauv5lqetKw2J5E7UooSTcHbZsxwkquWFnO6j1hj35/odS4P8AcbCOVssenZ+THkLOR7kxx5l7e5g==", + "license": "GPL-3.0-or-later", + "dependencies": { + "@metamask/providers": "16.0.0" + }, + "peerDependencies": { + "@multiversx/sdk-core": ">= 13.0.0" + } + }, + "node_modules/@multiversx/sdk-native-auth-client": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-native-auth-client/-/sdk-native-auth-client-1.0.9.tgz", + "integrity": "sha512-q1/cDRKz7QQsr8lQskUsfGkqJbIut772/MBX52Td4OTGg/G1HAm2xsELe+06y7L537A2rqz5/W9KkJ5yWt968g==", + "license": "GPL-3.0-or-later", + "dependencies": { + "axios": "^1.7.4" + } + }, + "node_modules/@multiversx/sdk-opera-provider": { + "version": "1.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-opera-provider/-/sdk-opera-provider-1.0.0-alpha.1.tgz", + "integrity": "sha512-5hrqn+kNpuy/S6eV5wh5mE4lvQo0PduZ7fLsh/2Srcaz3K5kM5lE1VyQmWk4DTxToZSldrGbgWz/olezoC6fPg==", + "license": "GPL-3.0-or-later" + }, + "node_modules/@multiversx/sdk-passkey-provider": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-passkey-provider/-/sdk-passkey-provider-1.0.9.tgz", + "integrity": "sha512-9CNQICB7xPUJYzLKYBLzxTDhiwkcmfCxijnP1otjuQDY4AIKyHctVTXfF0C9WreMs0HlNMzJve81+FonhoPjYA==", + "license": "GPL-3.0-or-later", + "dependencies": { + "@noble/ed25519": "2.1.0", + "@types/jest": "29.5.11", + "short-unique-id": "5.2.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@multiversx/sdk-core": ">= 12.18.0", + "@multiversx/sdk-dapp-utils": ">= 0.1.0", + "@multiversx/sdk-wallet": ">=4.5.1" + } + }, + "node_modules/@multiversx/sdk-passkey-provider/node_modules/@noble/ed25519": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-2.1.0.tgz", + "integrity": "sha512-KM4qTyXPinyCgMzeYJH/UudpdL+paJXtY3CHtHYZQtBkS8MZoPr4rOikZllIutJe0d06QDQKisyn02gxZ8TcQA==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@multiversx/sdk-transaction-decoder": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-transaction-decoder/-/sdk-transaction-decoder-1.0.2.tgz", + "integrity": "sha512-j43QsKquu8N51WLmVlJ7dV2P3A1448R7/ktvl8r3i6wRMpfdtzDPNofTdHmMRT7DdQdvs4+RNgz8hVKL11Etsw==", + "license": "MIT", + "dependencies": { + "bech32": "^2.0.0" + } + }, + "node_modules/@multiversx/sdk-transaction-decoder/node_modules/bech32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==", + "license": "MIT" + }, + "node_modules/@multiversx/sdk-wallet": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-wallet/-/sdk-wallet-4.6.0.tgz", + "integrity": "sha512-SxO/hBTnAB+uWH4MJdwKLvfRk8cxdA8zi6JC4I9j1+V+XQJpfs2YHCvMmaVyIAqjmSg9i7Uk61exrDkR3bjPAw==", + "license": "MIT", + "dependencies": { + "@multiversx/sdk-bls-wasm": "0.3.5", + "@noble/ed25519": "1.7.3", + "@noble/hashes": "1.3.0", + "bech32": "1.1.4", + "bip39": "3.1.0", + "blake2b": "2.1.3", + "ed25519-hd-key": "1.1.2", + "ed2curve": "0.3.0", + "keccak": "3.0.1", + "scryptsy": "2.1.0", + "tweetnacl": "1.0.3", + "uuid": "8.3.2" + } + }, + "node_modules/@multiversx/sdk-wallet-connect-provider": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-wallet-connect-provider/-/sdk-wallet-connect-provider-4.1.4.tgz", + "integrity": "sha512-nAA+LSK+28iBrTbmZOkHaMavQiGr6sdAK9Vhjf5aPpsWYAXOZijqAQnuYg9aDcOT2yCF4L4Ngzzj1qKlW1TpFA==", + "license": "MIT", + "dependencies": { + "@walletconnect/sign-client": "2.17.0", + "@walletconnect/utils": "2.17.0", + "bech32": "1.1.4" + }, + "peerDependencies": { + "@multiversx/sdk-core": ">= 12.8.0" + } + }, + "node_modules/@multiversx/sdk-wallet/node_modules/keccak": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz", + "integrity": "sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@multiversx/sdk-web-wallet-cross-window-provider": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-web-wallet-cross-window-provider/-/sdk-web-wallet-cross-window-provider-1.0.0.tgz", + "integrity": "sha512-xqdKCFpBCxNcp4aSwC2FLbks2Ii2uy5YpHnqR8qnqCnjH6TqdGZ1xKzQauZsiYqseVueVTmynK28w9pTOZ0Oqg==", + "license": "GPL-3.0-or-later", + "dependencies": { + "@types/jest": "^29.5.11", + "@types/qs": "6.9.10", + "qs": "6.11.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@multiversx/sdk-core": ">= 12.18.0", + "@multiversx/sdk-dapp-utils": ">= 0.1.0" + } + }, + "node_modules/@multiversx/sdk-web-wallet-cross-window-provider/node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@multiversx/sdk-web-wallet-iframe-provider": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-web-wallet-iframe-provider/-/sdk-web-wallet-iframe-provider-1.0.3.tgz", + "integrity": "sha512-viKc7nmy66W+kjX6JIPm/adiwieX5K82Cuy9JtFS1xTx/Q7uZJOxktqC2+MIUPKudiYKWY1N07WaM5mzTIu5gQ==", + "license": "GPL-3.0-or-later", + "dependencies": { + "@types/jest": "^29.5.11", + "@types/qs": "6.9.10", + "qs": "6.11.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@multiversx/sdk-core": ">= 12.18.0", + "@multiversx/sdk-dapp-utils": ">= 0.1.0" + } + }, + "node_modules/@multiversx/sdk-web-wallet-iframe-provider/node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@multiversx/sdk-web-wallet-provider": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-web-wallet-provider/-/sdk-web-wallet-provider-3.2.1.tgz", + "integrity": "sha512-f0CHVsBephFhoQtCAx9y2WhiIZEaNgn0oVa+sZNkgILcXU53Gm8Rj8wMfM0SDimHBYcXCcBDedyLCU3MioOjoQ==", + "license": "GPL-3.0-or-later", + "dependencies": { + "qs": "6.10.3" + }, + "peerDependencies": { + "@multiversx/sdk-core": ">= 12.18.0" + } + }, + "node_modules/@multiversx/sdk-webview-provider": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-webview-provider/-/sdk-webview-provider-1.0.0.tgz", + "integrity": "sha512-nxC1BaP319ApbJb51QNi7W9BwUrS7V311H7lVwIUgOgVFrCwdttc/rlvVDrmieQIupqSSCWWKf1/cvbzt621aw==", + "license": "GPL-3.0-or-later", + "peerDependencies": { + "@multiversx/sdk-core": ">= 12.1.0", + "@multiversx/sdk-dapp-utils": ">= 0.1.0" + } + }, + "node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/ed25519": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.3.tgz", + "integrity": "sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@noble/hashes": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", + "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, + "node_modules/@reduxjs/toolkit": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.2.tgz", + "integrity": "sha512-CtPw5TkN1pHRigMFCOS/0qg3b/yfPV5qGCsltVnIz7bx4PKTJlGHYfIxm97qskLknMzuGfjExaYdXJ77QTL0vg==", + "license": "MIT", + "dependencies": { + "immer": "^9.0.7", + "redux": "^4.1.2", + "redux-thunk": "^2.4.1", + "reselect": "^4.1.5" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.0-beta" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@reduxjs/toolkit/node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", + "license": "MIT" + }, + "node_modules/@scure/base": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz", + "integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "license": "MIT" + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@stablelib/aead": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/aead/-/aead-1.0.1.tgz", + "integrity": "sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg==", + "license": "MIT" + }, + "node_modules/@stablelib/binary": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", + "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", + "license": "MIT", + "dependencies": { + "@stablelib/int": "^1.0.1" + } + }, + "node_modules/@stablelib/bytes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz", + "integrity": "sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==", + "license": "MIT" + }, + "node_modules/@stablelib/chacha": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/chacha/-/chacha-1.0.1.tgz", + "integrity": "sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg==", + "license": "MIT", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/chacha20poly1305": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/chacha20poly1305/-/chacha20poly1305-1.0.1.tgz", + "integrity": "sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA==", + "license": "MIT", + "dependencies": { + "@stablelib/aead": "^1.0.1", + "@stablelib/binary": "^1.0.1", + "@stablelib/chacha": "^1.0.1", + "@stablelib/constant-time": "^1.0.1", + "@stablelib/poly1305": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/constant-time/-/constant-time-1.0.1.tgz", + "integrity": "sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==", + "license": "MIT" + }, + "node_modules/@stablelib/ed25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.3.tgz", + "integrity": "sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==", + "license": "MIT", + "dependencies": { + "@stablelib/random": "^1.0.2", + "@stablelib/sha512": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/hash": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", + "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==", + "license": "MIT" + }, + "node_modules/@stablelib/hkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/hkdf/-/hkdf-1.0.1.tgz", + "integrity": "sha512-SBEHYE16ZXlHuaW5RcGk533YlBj4grMeg5TooN80W3NpcHRtLZLLXvKyX0qcRFxf+BGDobJLnwkvgEwHIDBR6g==", + "license": "MIT", + "dependencies": { + "@stablelib/hash": "^1.0.1", + "@stablelib/hmac": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/hmac": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/hmac/-/hmac-1.0.1.tgz", + "integrity": "sha512-V2APD9NSnhVpV/QMYgCVMIYKiYG6LSqw1S65wxVoirhU/51ACio6D4yDVSwMzuTJXWZoVHbDdINioBwKy5kVmA==", + "license": "MIT", + "dependencies": { + "@stablelib/constant-time": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/int": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", + "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==", + "license": "MIT" + }, + "node_modules/@stablelib/keyagreement": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz", + "integrity": "sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==", + "license": "MIT", + "dependencies": { + "@stablelib/bytes": "^1.0.1" + } + }, + "node_modules/@stablelib/poly1305": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/poly1305/-/poly1305-1.0.1.tgz", + "integrity": "sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA==", + "license": "MIT", + "dependencies": { + "@stablelib/constant-time": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/random": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", + "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", + "license": "MIT", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/sha256": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/sha256/-/sha256-1.0.1.tgz", + "integrity": "sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ==", + "license": "MIT", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/sha512": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz", + "integrity": "sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==", + "license": "MIT", + "dependencies": { + "@stablelib/binary": "^1.0.1", + "@stablelib/hash": "^1.0.1", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@stablelib/wipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", + "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==", + "license": "MIT" + }, + "node_modules/@stablelib/x25519": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@stablelib/x25519/-/x25519-1.0.3.tgz", + "integrity": "sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==", + "license": "MIT", + "dependencies": { + "@stablelib/keyagreement": "^1.0.1", + "@stablelib/random": "^1.0.2", + "@stablelib/wipe": "^1.0.1" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", + "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", + "license": "MIT", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", + "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.9.10", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", + "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", + "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", + "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "license": "MIT" + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT" + }, + "node_modules/@walletconnect/core": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.17.0.tgz", + "integrity": "sha512-On+uSaCfWdsMIQsECwWHZBmUXfrnqmv6B8SXRRuTJgd8tUpEvBkLQH4X7XkSm3zW6ozEkQTCagZ2ox2YPn3kbw==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-provider": "1.0.14", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/jsonrpc-ws-connection": "1.0.14", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.0.4", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.17.0", + "@walletconnect/utils": "2.17.0", + "events": "3.3.0", + "lodash.isequal": "4.5.0", + "uint8arrays": "3.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@walletconnect/core/node_modules/@walletconnect/keyvaluestorage": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.1.1.tgz", + "integrity": "sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==", + "license": "MIT", + "dependencies": { + "@walletconnect/safe-json": "^1.0.1", + "idb-keyval": "^6.2.1", + "unstorage": "^1.9.0" + }, + "peerDependencies": { + "@react-native-async-storage/async-storage": "1.x" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@walletconnect/core/node_modules/unstorage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.15.0.tgz", + "integrity": "sha512-m40eHdGY/gA6xAPqo8eaxqXgBuzQTlAKfmB1iF7oCKXE1HfwHwzDJBywK+qQGn52dta+bPlZluPF7++yR3p/bg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^4.0.3", + "destr": "^2.0.3", + "h3": "^1.15.0", + "lru-cache": "^10.4.3", + "node-fetch-native": "^1.6.6", + "ofetch": "^1.4.1", + "ufo": "^1.5.4" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6.0.3", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/kv": "^1.0.1", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/@walletconnect/environment": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/environment/-/environment-1.0.1.tgz", + "integrity": "sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==", + "license": "MIT", + "dependencies": { + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/events": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/events/-/events-1.0.1.tgz", + "integrity": "sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==", + "license": "MIT", + "dependencies": { + "keyvaluestorage-interface": "^1.0.0", + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/heartbeat": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@walletconnect/heartbeat/-/heartbeat-1.2.2.tgz", + "integrity": "sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==", + "license": "MIT", + "dependencies": { + "@walletconnect/events": "^1.0.1", + "@walletconnect/time": "^1.0.2", + "events": "^3.3.0" + } + }, + "node_modules/@walletconnect/jsonrpc-provider": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-provider/-/jsonrpc-provider-1.0.14.tgz", + "integrity": "sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==", + "license": "MIT", + "dependencies": { + "@walletconnect/jsonrpc-utils": "^1.0.8", + "@walletconnect/safe-json": "^1.0.2", + "events": "^3.3.0" + } + }, + "node_modules/@walletconnect/jsonrpc-types": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.4.tgz", + "integrity": "sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==", + "license": "MIT", + "dependencies": { + "events": "^3.3.0", + "keyvaluestorage-interface": "^1.0.0" + } + }, + "node_modules/@walletconnect/jsonrpc-utils": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.8.tgz", + "integrity": "sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==", + "license": "MIT", + "dependencies": { + "@walletconnect/environment": "^1.0.1", + "@walletconnect/jsonrpc-types": "^1.0.3", + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/jsonrpc-ws-connection": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.14.tgz", + "integrity": "sha512-Jsl6fC55AYcbkNVkwNM6Jo+ufsuCQRqViOQ8ZBPH9pRREHH9welbBiszuTLqEJiQcO/6XfFDl6bzCJIkrEi8XA==", + "license": "MIT", + "dependencies": { + "@walletconnect/jsonrpc-utils": "^1.0.6", + "@walletconnect/safe-json": "^1.0.2", + "events": "^3.3.0", + "ws": "^7.5.1" + } + }, + "node_modules/@walletconnect/logger": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@walletconnect/logger/-/logger-2.1.2.tgz", + "integrity": "sha512-aAb28I3S6pYXZHQm5ESB+V6rDqIYfsnHaQyzFbwUUBFY4H0OXx/YtTl8lvhUNhMMfb9UxbwEBS253TlXUYJWSw==", + "license": "MIT", + "dependencies": { + "@walletconnect/safe-json": "^1.0.2", + "pino": "7.11.0" + } + }, + "node_modules/@walletconnect/relay-api": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@walletconnect/relay-api/-/relay-api-1.0.11.tgz", + "integrity": "sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==", + "license": "MIT", + "dependencies": { + "@walletconnect/jsonrpc-types": "^1.0.2" + } + }, + "node_modules/@walletconnect/relay-auth": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@walletconnect/relay-auth/-/relay-auth-1.0.4.tgz", + "integrity": "sha512-kKJcS6+WxYq5kshpPaxGHdwf5y98ZwbfuS4EE/NkQzqrDFm5Cj+dP8LofzWvjrrLkZq7Afy7WrQMXdLy8Sx7HQ==", + "license": "MIT", + "dependencies": { + "@stablelib/ed25519": "^1.0.2", + "@stablelib/random": "^1.0.1", + "@walletconnect/safe-json": "^1.0.1", + "@walletconnect/time": "^1.0.2", + "tslib": "1.14.1", + "uint8arrays": "^3.0.0" + } + }, + "node_modules/@walletconnect/safe-json": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/safe-json/-/safe-json-1.0.2.tgz", + "integrity": "sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==", + "license": "MIT", + "dependencies": { + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/sign-client": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.17.0.tgz", + "integrity": "sha512-sErYwvSSHQolNXni47L3Bm10ptJc1s1YoJvJd34s5E9h9+d3rj7PrhbiW9X82deN+Dm5oA8X9tC4xty1yIBrVg==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/core": "2.17.0", + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-utils": "1.0.8", + "@walletconnect/logger": "2.1.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.17.0", + "@walletconnect/utils": "2.17.0", + "events": "3.3.0" + } + }, + "node_modules/@walletconnect/time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@walletconnect/time/-/time-1.0.2.tgz", + "integrity": "sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==", + "license": "MIT", + "dependencies": { + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/types": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.17.0.tgz", + "integrity": "sha512-i1pn9URpvt9bcjRDkabuAmpA9K7mzyKoLJlbsAujRVX7pfaG7wur7u9Jz0bk1HxvuABL5LHNncTnVKSXKQ5jZA==", + "license": "Apache-2.0", + "dependencies": { + "@walletconnect/events": "1.0.1", + "@walletconnect/heartbeat": "1.2.2", + "@walletconnect/jsonrpc-types": "1.0.4", + "@walletconnect/keyvaluestorage": "1.1.1", + "@walletconnect/logger": "2.1.2", + "events": "3.3.0" + } + }, + "node_modules/@walletconnect/types/node_modules/@walletconnect/keyvaluestorage": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.1.1.tgz", + "integrity": "sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==", + "license": "MIT", + "dependencies": { + "@walletconnect/safe-json": "^1.0.1", + "idb-keyval": "^6.2.1", + "unstorage": "^1.9.0" + }, + "peerDependencies": { + "@react-native-async-storage/async-storage": "1.x" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@walletconnect/types/node_modules/unstorage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.15.0.tgz", + "integrity": "sha512-m40eHdGY/gA6xAPqo8eaxqXgBuzQTlAKfmB1iF7oCKXE1HfwHwzDJBywK+qQGn52dta+bPlZluPF7++yR3p/bg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^4.0.3", + "destr": "^2.0.3", + "h3": "^1.15.0", + "lru-cache": "^10.4.3", + "node-fetch-native": "^1.6.6", + "ofetch": "^1.4.1", + "ufo": "^1.5.4" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6.0.3", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/kv": "^1.0.1", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/@walletconnect/utils": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.17.0.tgz", + "integrity": "sha512-1aeQvjwsXy4Yh9G6g2eGmXrEl+BzkNjHRdCrGdMYqFTFa8ROEJfTGsSH3pLsNDlOY94CoBUvJvM55q/PMoN/FQ==", + "license": "Apache-2.0", + "dependencies": { + "@stablelib/chacha20poly1305": "1.0.1", + "@stablelib/hkdf": "1.0.1", + "@stablelib/random": "1.0.2", + "@stablelib/sha256": "1.0.1", + "@stablelib/x25519": "1.0.3", + "@walletconnect/relay-api": "1.0.11", + "@walletconnect/relay-auth": "1.0.4", + "@walletconnect/safe-json": "1.0.2", + "@walletconnect/time": "1.0.2", + "@walletconnect/types": "2.17.0", + "@walletconnect/window-getters": "1.0.1", + "@walletconnect/window-metadata": "1.0.1", + "detect-browser": "5.3.0", + "elliptic": "^6.5.7", + "query-string": "7.1.3", + "uint8arrays": "3.1.0" + } + }, + "node_modules/@walletconnect/window-getters": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.1.tgz", + "integrity": "sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==", + "license": "MIT", + "dependencies": { + "tslib": "1.14.1" + } + }, + "node_modules/@walletconnect/window-metadata": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.1.tgz", + "integrity": "sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==", + "license": "MIT", + "dependencies": { + "@walletconnect/window-getters": "^1.0.1", + "tslib": "1.14.1" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", + "license": "MIT" + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/bip39": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz", + "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==", + "license": "ISC", + "dependencies": { + "@noble/hashes": "^1.2.0" + } + }, + "node_modules/blake2b": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/blake2b/-/blake2b-2.1.3.tgz", + "integrity": "sha512-pkDss4xFVbMb4270aCyGD3qLv92314Et+FsKzilCLxDz5DuZ2/1g3w4nmBbu6nKApPspnjG7JcwTjGZnduB1yg==", + "license": "ISC", + "dependencies": { + "blake2b-wasm": "^1.1.0", + "nanoassert": "^1.0.0" + } + }, + "node_modules/blake2b-wasm": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/blake2b-wasm/-/blake2b-wasm-1.1.7.tgz", + "integrity": "sha512-oFIHvXhlz/DUgF0kq5B1CqxIDjIJwh9iDeUUGQUcvgiGz7Wdw03McEO7CfLBy7QKGdsydcMCgO9jFNBAFCtFcA==", + "license": "MIT", + "dependencies": { + "nanoassert": "^1.0.0" + } + }, + "node_modules/bn.js": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", + "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "license": "MIT" + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chai": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.0.tgz", + "integrity": "sha512-kDZ7MZyM6Q1DhR9jy7dalKohXQ2yrlXkk59CR52aRKxJrobmlBNqnFQxX9xOX8w+4mz8SYlKJa/7D7ddltFXCw==", + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.0.0", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cipher-base": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz", + "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/classnames": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==", + "license": "MIT", + "optional": true + }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/cookie-es": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", + "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/crossws": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.4.tgz", + "integrity": "sha512-uj0O1ETYX1Bh6uSgktfPvwDiPYGQ3aI4qVsaC/LWpkIzGj1nUYm5FK3K+t11oOlpN01lGbprFCH4wBlKdJjVgw==", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/destr": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.3.tgz", + "integrity": "sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==", + "license": "MIT" + }, + "node_modules/detect-browser": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.3.0.tgz", + "integrity": "sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==", + "license": "MIT" + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT", + "optional": true + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/ed25519-hd-key": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ed25519-hd-key/-/ed25519-hd-key-1.1.2.tgz", + "integrity": "sha512-/0y9y6N7vM6Kj5ASr9J9wcMVDTtygxSOvYX+PJiMD7VcxCx2G03V5bLRl8Dug9EgkLFsLhGqBtQWQRcElEeWTA==", + "license": "MIT", + "dependencies": { + "bip39": "3.0.2", + "create-hmac": "1.1.7", + "tweetnacl": "1.0.3" + } + }, + "node_modules/ed25519-hd-key/node_modules/@types/node": { + "version": "11.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", + "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==", + "license": "MIT" + }, + "node_modules/ed25519-hd-key/node_modules/bip39": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.2.tgz", + "integrity": "sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ==", + "license": "ISC", + "dependencies": { + "@types/node": "11.11.6", + "create-hash": "^1.1.0", + "pbkdf2": "^3.0.9", + "randombytes": "^2.0.1" + } + }, + "node_modules/ed2curve": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ed2curve/-/ed2curve-0.3.0.tgz", + "integrity": "sha512-8w2fmmq3hv9rCrcI7g9hms2pMunQr1JINfcjwR9tAyZqhtyaMN991lF/ZfHfr5tzZQ8c7y7aBgZbjfbd0fjFwQ==", + "license": "Unlicense", + "dependencies": { + "tweetnacl": "1.x.x" + } + }, + "node_modules/elliptic": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", + "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", + "optional": true + }, + "node_modules/encode-utf8": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", + "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==", + "license": "MIT", + "optional": true + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz", + "integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.7.tgz", + "integrity": "sha512-P+jDFbvK6lE3n1OL+q9KuzdOFWkkZ/cMV9gol/SbVfpyqfvrfrFTOFJ6fQm2VC3PZHlU3QPhVwmbsCnauHF2MQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "license": "MIT", + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/extension-port-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/extension-port-stream/-/extension-port-stream-3.0.0.tgz", + "integrity": "sha512-an2S5quJMiy5bnZKEf6AkfH/7r8CzHvhchU40gxN+OM6HPhe7Z9T1FUychcf2M9PpPOO0Hf7BAEfJkw2TDIBDw==", + "license": "ISC", + "dependencies": { + "readable-stream": "^3.6.2 || ^4.4.2", + "webextension-polyfill": ">=0.10.0 <1.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "optional": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "optional": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/h3": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.1.tgz", + "integrity": "sha512-+ORaOBttdUm1E2Uu/obAyCguiI7MbBvsLTndc3gyK3zU+SYLoZXlyCP9Xgy0gikkGufFLTZXCXD6+4BsufnmHA==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.2", + "crossws": "^0.3.3", + "defu": "^6.1.4", + "destr": "^2.0.3", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.0", + "radix3": "^1.1.2", + "ufo": "^1.5.4", + "uncrypto": "^0.1.3" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/idb-keyval": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", + "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==", + "license": "Apache-2.0" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/keccak": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", + "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/keyvaluestorage-interface": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", + "integrity": "sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==", + "license": "MIT" + }, + "node_modules/linkify-react": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/linkify-react/-/linkify-react-4.0.2.tgz", + "integrity": "sha512-WFHnwOUo6EeKwrQQy1d+UjeKsv+SPQ9toPpaRIXHV1CMo+0kgZBSIsEBxQrFQIEy7WD20QD+sPwNNaJJpynN6g==", + "license": "MIT", + "peerDependencies": { + "linkifyjs": "^4.0.0", + "react": ">= 15.0.0" + } + }, + "node_modules/linkifyjs": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.0.2.tgz", + "integrity": "sha512-/VSoCZiglX0VMsXmL5PN3lRg45M86lrD9PskdkA2abWaTKap1bIcJ11LS4EE55bcUl9ZOR4eZ792UtQ9E/5xLA==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "optional": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", + "deprecated": "This package is deprecated. Use destructuring assignment syntax instead.", + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz", + "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==", + "license": "Apache-2.0" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multiformats": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", + "license": "(Apache-2.0 AND MIT)" + }, + "node_modules/nanoassert": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz", + "integrity": "sha512-C40jQ3NzfkP53NsO8kEOFd79p4b9kDXQMwgiY1z8ZwrDZgUyom0AHwGegF4Dm99L+YoYhuaB0ceerUcXmqr1rQ==", + "license": "ISC" + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "license": "MIT" + }, + "node_modules/node-fetch-native": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz", + "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==", + "license": "MIT" + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-mock-http": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.0.tgz", + "integrity": "sha512-0uGYQ1WQL1M5kKvGRXWQ3uZCHtLTO8hln3oBjIusM75WoesZ909uQJs/Hb946i2SS+Gsrhkaa6iAO17jRIv6DQ==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ofetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.4.1.tgz", + "integrity": "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.3", + "node-fetch-native": "^1.6.4", + "ufo": "^1.5.4" + } + }, + "node_modules/on-exit-leak-free": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz", + "integrity": "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==", + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "optional": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "optional": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "license": "MIT", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pino": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-7.11.0.tgz", + "integrity": "sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.0.0", + "on-exit-leak-free": "^0.2.0", + "pino-abstract-transport": "v0.5.0", + "pino-std-serializers": "^4.0.0", + "process-warning": "^1.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.1.0", + "safe-stable-stringify": "^2.1.0", + "sonic-boom": "^2.2.1", + "thread-stream": "^0.15.1" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz", + "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==", + "license": "MIT", + "dependencies": { + "duplexify": "^4.1.2", + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", + "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==", + "license": "MIT" + }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "license": "MIT" + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/pony-cause": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", + "integrity": "sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==", + "license": "0BSD", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-warning": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", + "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==", + "license": "MIT" + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "optional": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT", + "optional": true + }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/qrcode": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.0.tgz", + "integrity": "sha512-9MgRpgVc+/+47dFvQeD6U2s0Z92EsKzcHogtum4QB+UNd025WOJSHvn/hjk9xmzj7Stj95CyUAs31mrjxliEsQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "dijkstrajs": "^1.0.1", + "encode-utf8": "^1.0.3", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-idle-timer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-idle-timer/-/react-idle-timer-5.0.0.tgz", + "integrity": "sha512-41ZoZnGJQH4P7v22pWi5rx1kBEjcOdHIB9OFEvyKtj4UX3T4hB3rJya566QZsFSYMmOA9+rgaXc1JSXy7c+g9A==", + "license": "MIT", + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-redux": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.2.tgz", + "integrity": "sha512-nBwiscMw3NoP59NFCXFf02f8xdo+vSHT/uZ1ldDwF7XaTpzm+Phk97VT4urYBl5TYAPNVaFm12UHAEyzkpNzRA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/real-require": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz", + "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-persist": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", + "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", + "license": "MIT", + "peerDependencies": { + "redux": ">4.0.0" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "license": "MIT", + "peerDependencies": { + "redux": "^4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC", + "optional": true + }, + "node_modules/reselect": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz", + "integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/scryptsy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", + "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC", + "optional": true + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "license": "BSD-3-Clause", + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/short-unique-id": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-5.2.0.tgz", + "integrity": "sha512-cMGfwNyfDZ/nzJ2k2M+ClthBIh//GlZl1JEf47Uoa9XR11bz8Pa2T2wQO4bVrRdH48LrIDWJahQziKo3MjhsWg==", + "license": "Apache-2.0", + "bin": { + "short-unique-id": "bin/short-unique-id", + "suid": "bin/short-unique-id" + } + }, + "node_modules/shx": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", + "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.3", + "shelljs": "^0.8.5" + }, + "bin": { + "shx": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/socket.io-client": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.6.1.tgz", + "integrity": "sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.4.0", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/sonic-boom": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz", + "integrity": "sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT" + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swr": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.0.tgz", + "integrity": "sha512-AjqHOv2lAhkuUdIiBu9xbuettzAzWXmCEcLONNKJRba87WAefz8Ca9d6ds/SzrPc235n1IxWYdhJ2zF3MNUaoQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/thread-stream": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-0.15.2.tgz", + "integrity": "sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==", + "license": "MIT", + "dependencies": { + "real-require": "^0.1.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "license": "MIT" + }, + "node_modules/uint8arrays": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.1.0.tgz", + "integrity": "sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog==", + "license": "MIT", + "dependencies": { + "multiformats": "^9.4.2" + } + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, + "node_modules/use-sync-external-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/webextension-polyfill": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz", + "integrity": "sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==", + "license": "MPL-2.0" + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC", + "optional": true + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC", + "optional": true + }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "optional": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/cookbook/package.json b/cookbook/package.json new file mode 100644 index 00000000..7d879542 --- /dev/null +++ b/cookbook/package.json @@ -0,0 +1,9 @@ +{ + "type": "module", + "dependencies": { + "@multiversx/sdk-core": "14.0.0", + "@multiversx/sdk-dapp": "2.40.11", + "axios": "1.7.7", + "chai": "5.1.0" + } +} diff --git a/cookbook/relayed.js b/cookbook/relayed.js new file mode 100644 index 00000000..fb1aa950 --- /dev/null +++ b/cookbook/relayed.js @@ -0,0 +1,149 @@ +import { Account, Address, DevnetEntrypoint, Transaction } from "@multiversx/sdk-core"; // md-ignore +import path from 'path'; // md-ignore +// ### Relayed transactions +// We are currently on the `third iteration (V3)` of relayed transactions. V1 and V2 will soon be deactivated, so we will focus on V3. + +// For V3, two new fields have been added to transactions: +// - relayer +// - relayerSignature + +// Signing Process: +// 1. The relayer must be set before the sender signs the transaction. +// 2. Once the sender has signed, the relayer can also sign the transaction and broadcast it. + +// **Important Consideration**: +// Relayed V3 transactions require an additional `50,000` gas. +// Let’s see how to create a relayed transaction: + +// ```js +{ + const walletsPath = path.join("src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + const bob = await Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = new Transaction({ + chainID: "D", + sender: alice.address, + receiver: bob, + relayer: carol.address, + gasLimit: 110_000n, + data: Buffer.from("hello"), + nonce: alice.getNonceThenIncrement() + }); + + // sender signs the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // relayer signs the transaction // md-as-comment + transaction.RelayerSignature = await carol.signTransaction(transaction); + + // broadcast the transaction // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Creating relayed transactions using controllers +// We can create relayed transactions using any of the available controllers. +// Each controller includes a relayer argument, which must be set if we want to create a relayed transaction. + +// Let’s issue a fungible token using a relayed transaction: + +// ```js +{ + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.creatTokenManagementController(); + + // create the issuer of the token // md-as-comment + const walletsPath = path.join("src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // Carol will be our relayer, that means she is paying the gas for the transaction // md-as-comment + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + guardian: carol.address, + }, + ); + + // relayer also signs the transaction // md-as-comment + transaction.relayerSignature = await carol.signTransaction(transaction); + + // broadcast the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// #### Creating relayed transactions using factories +// Unlike controllers, `transaction factories` do not have a `relayer` argument. Instead, the **relayer must be set after creating the transaction**. +// This approach is beneficial because the **transaction is not signed by the sender at the time of creation**, allowing flexibility in setting the relayer before signing. + +// Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: + +// ```js +{ + // create the entrypoint and the token management factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.creatTokenManagementController(); + + // create the issuer of the token // md-as-comment + const walletsPath = path.join("src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // carol will be our relayer, that means she is paying the gas for the transaction // md-as-comment + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + const transaction = await factory.createTransactionForIssuingFungible( + alice.address, + { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }, + ); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // set the relayer // md-as-comment + transaction.relayer = carol.address; + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // relayer also signs the transaction // md-as-comment + transaction.relayerSignature = await carol.signTransaction(transaction); + + // broadcast the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` diff --git a/cookbook/signingObjects.js b/cookbook/signingObjects.js new file mode 100644 index 00000000..30c62a76 --- /dev/null +++ b/cookbook/signingObjects.js @@ -0,0 +1,118 @@ + +import { Account, Address, Message, MessageComputer, Transaction, TransactionComputer, UserSecretKey } from "@multiversx/sdk-core"; // md-ignore +import path from 'path'; // md-ignore +// ## Signing objects + +// Signing is done using an account's secret key. To simplify this process, we provide wrappers like [Account](#creating-accounts), which streamline signing operations. +// First, we'll explore how to sign using an Account, followed by signing directly with a secret key. + +// #### Signing a Transaction using an Account +// We are going to assume we have an account at this point. If you don't, feel free to check out the [creating an account](#creating-accounts) section. +// ```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + chainID: "D", + sender: alice.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + gasLimit: 50000n, + nonce: 90n + }); + + transaction.signature = await alice.signTransaction(transaction); + console.log(transaction.toPlainObject()); +} +// ``` + +// #### Signing a Transaction using a SecretKey +// ```js +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = UserSecretKey.fromString(secretKeyHex); + const publickKey = secretKey.generatePublicKey(); + + const transaction = new Transaction({ + nonce: 90n, + sender: publickKey.toAddress(), + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D" + }); + + // serialize the transaction // md-as-comment + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForSigning(transaction); + + // apply the signature on the transaction // md-as-comment + transaction.signature = await secretKey.sign(serializedTransaction); + + console.log(transaction.toPlainObject()); +} +// ``` + +// #### Signing a Transaction by hash +// ```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + nonce: 90n, + sender: publickKey.toAddress(), + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D" + }); + + const transactionComputer = new TransactionComputer(); + + // sets the least significant bit of the options field to `1` // md-as-comment + transactionComputer.applyOptionsForHashSigning(transaction); + + // compute a keccak256 hash for signing // md-as-comment + const hash = transactionComputer.computeHashForSigning(transaction); + + // sign and apply the signature on the transaction // md-as-comment + transaction.signature = await alice.signTransaction(hash); + + console.log(transaction.toPlainObject()); +} +// ``` + +// #### Signing a Message using an Account: +// ```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: alice.address + }); + + message.signature = await alice.signMessage(message); +} +// ``` + +// #### Signing a Message using an SecretKey: +// ```js +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = UserSecretKey.fromString(secretKeyHex); + const publicKey = secretKey.generatePublicKey(); + + const messageComputer = new MessageComputer(); + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: publicKey.toAddress() + }); + // serialized the message + const serialized = messageComputer.computeBytesForSigning(message); + + message.signature = await secretKey.sign(serialized); +} +// ``` diff --git a/cookbook/smartContracts.js b/cookbook/smartContracts.js new file mode 100644 index 00000000..a37e256b --- /dev/null +++ b/cookbook/smartContracts.js @@ -0,0 +1,533 @@ +import { AbiRegistry, Account, Address, AddressComputer, BigUIntValue, DevnetEntrypoint, Field, SmartContractTransactionsOutcomeParser, Struct, TokenIdentifierValue, U32Value, U64Value } from "@multiversx/sdk-core"; // md-ignore +import axios from "axios"; // md-ignore +import { promises } from "fs"; // md-ignore +import path from 'path'; // md-ignore +// ### Smart Contracts + +// #### Contract ABIs + +// A contract's ABI (Application Binary Interface) describes the endpoints, data structures, and events that the contract exposes. +// While interactions with the contract are possible without the ABI, they are much easier to implement when the definitions are available. + +// #### Loading the ABI from a file +// ```js +{ + let abiJson = await promises.readFile("../contracts/adder.abi.json", { encoding: "utf8" }); + let abiObj = JSON.parse(abiJson); + let abi = AbiRegistry.create(abiObj); +} +// ``` + +// #### Loading the ABI from an URL + +// ```js +{ + const response = await axios.get("https://github.com/multiversx/mx-sdk-js-core/raw/main/src/testdata/adder.abi.json"); + abi = AbiRegistry.create(response.data); +} +// ``` + +// #### Manually construct the ABI + +// If an ABI file isn’t available, but you know the contract’s endpoints and data types, you can manually construct the ABI. + +// ```js +{ + abi = AbiRegistry.create({ + "endpoints": [{ + "name": "add", + "inputs": [], + "outputs": [] + }] + }); +} +// ``` + +// ```js +{ + abi = AbiRegistry.create({ + "endpoints": [ + { + "name": "foo", + "inputs": [ + { "type": "BigUint" }, + { "type": "u32" }, + { "type": "Address" } + ], + "outputs": [ + { "type": "u32" } + ] + }, + { + "name": "bar", + "inputs": [ + { "type": "counted-variadic" }, + { "type": "variadic" } + ], + "outputs": [] + } + ] + }); +} +// ``` + +// ### Smart Contract deployments +// For creating smart contract deployment transactions, we have two options: a controller and a factory. Both function similarly to the ones used for token transfers. +// When creating transactions that interact with smart contracts, it's recommended to provide the ABI file to the controller or factory if possible. +// This allows arguments to be passed as native Javascript values. If the ABI is not available, but we know the expected data types, we can pass arguments as typed values (e.g., `BigUIntValue`, `ListValue`, `StructValue`, etc.) or as raw bytes. + +// #### Deploying a Smart Contract Using the Controller + +// ```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); + + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); + + // load the contract bytecode + const bytecode = await promises.readFile("../contracts/adder.wasm"); + // load the abi file + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + + const controller = entrypoint.createSmartContractController(abi); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment + let args = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment + args = [42]; + + const deployTransaction = await controller.createTransactionForDeploy( + sender, + sender.getNonceThenIncrement(), + { + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }, + ); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(deployTransaction); +} +// ``` + +// md-insert:mixedTypedValuesAndNativeValues + +// #### Parsing contract deployment transactions + +// ```js +{ + // We use the transaction hash we got when broadcasting the transaction + const outcome = await controller.awaitCompletedDeploy(txHash); // waits for transaction completion and parses the result + const contractAddress = outcome.contracts[0].address; +} +// ``` + +// If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: + +// ```js +{ + // We use the transaction hash we got when broadcasting the transaction + // If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: + const transactionOnNetwork = await controller.awaitTransactionCompleted(txHash); + + // parsing the transaction + const outcome = await controller.parseDeploy(transactionOnNetwork); +} +// ``` + +// #### Computing the contract address + +// Even before broadcasting, at the moment you know the sender's address and the nonce for your deployment transaction, you can (deterministically) compute the (upcoming) address of the smart contract: + +// ```js +{ + const addressComputer = new AddressComputer(); + const contractAddress = addressComputer.computeContractAddress( + deployTransaction.sender, + deployTransaction.nonce + ); + + console.log("Contract address:", contractAddress.toBech32()); +} +// ``` + +// #### Deploying a Smart Contract using the factory +// After the transaction is created the nonce needs to be properly set and the transaction should be signed before broadcasting it. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTransfersTransactionsFactory(); + + // load the contract bytecode + const bytecode = await promises.readFile("../contracts/adder.wasm"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment + let args = [new BigUIntValue(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment + args = [42]; + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const deployTransaction = await factory.createTransactionForDeploy( + sender, + { + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }, + ); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(sender.address); + + // set the nonce + deployTransaction.nonce = alice.nonce; + + // sign the transaction + deployTransaction.signature = await alice.signTransaction(transaction); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(deployTransaction); + + // waiting for transaction to complete + const transactionOnNetwork = await entrypoint.awaitTransactionCompleted(txHash); + + // parsing transaction + const parser = new SmartContractTransactionsOutcomeParser(); + const parsedOutcome = parser.parseDeploy(transactionOnNetwork); + const contractAddress = parsedOutcome.contracts[0].address; + + console.log(contractAddress.toBech32()); +} +// ``` + +// ### Smart Contract calls + +// In this section we'll see how we can call an endpoint of our previously deployed smart contract using both approaches with the `controller` and the `factory`. + +// #### Calling a smart contract using the controller + +// ```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); + + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); + + // load the abi file + const abi = await loadAbiRegistry("src/testdata/adder.abi.json");; + const controller = entrypoint.createSmartContractController(abi); + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment + let args = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment + args = [42]; + + const transaction = await controller.createTransactionForExecute( + sender, + sender.getNonceThenIncrement(), + { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + }, + ); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + console.log(txHash); +} +// ``` + +// #### Parsing smart contract call transactions +// In our case, calling the add endpoint does not return anything, but similar to the example above, we could parse this transaction to get the output values of a smart contract call. + +// ```js +{ + // waits for transaction completion and parses the result + const parsedOutcome = controller.awaitCompletedExecute(transactionOnNetwork); + const values = parsedOutcome.contracts.values; +} +// ``` + +// #### Calling a smart contract and sending tokens (transfer & execute) +// Additionally, if an endpoint requires a payment when called, we can send tokens to the contract while creating a smart contract call transaction. +// Both EGLD and ESDT tokens or a combination of both can be sent. This functionality is supported by both the controller and the factory. + +// ```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); + + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); + + // load the abi file + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + + // get the smart contracts controller + const controller = entrypoint.createSmartContractController(abi); + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment + let args = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment + args = [42]; + + // creating the transfers + const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10 }); + const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); + + const secondToken = new Token({ identifier: "BAR-c80d29" }); + const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); + + const transaction = await controller.createTransactionForExecute( + sender, + sender.getNonceThenIncrement(), + { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + nativeTransferAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer] + }, + ); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + console.log(txHash); +} +// ``` + +// #### Calling a smart contract using the factory +// Let's create the same smart contract call transaction, but using the `factory`. + +// ```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // load the abi file + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + + // get the smart contracts controller + const controller = entrypoint.createSmartContractTransactionsFactory(abi); + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment + let args = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment + args = [42]; + + // creating the transfers + const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10 }); + const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); + + const secondToken = new Token({ identifier: "BAR-c80d29" }); + const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); + + const transaction = await controller.createTransactionForExecute( + sender, + { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + nativeTransferAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer] + }, + ); + + transaction.nonce = alice.getNonceThenIncrement(); + transaction.signature = await alice.signTransaction(transaction); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + console.log(txHash); +} +// ``` + +// #### Parsing transaction outcome +// As said before, the `add` endpoint we called does not return anything, but we could parse the outcome of smart contract call transactions, as follows: + +// ```js +{ + // load the abi file + const entrypoint = new DevnetEntrypoint(); + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + const parser = SmartContractTransactionsOutcomeParser({ abi }); + const transactionOnNetwork = entrypoint.getTransaction(txHash); + const outcome = parser.parseExecute(transactionOnNetwork); +} +// ``` + +// #### Decoding transaction events +// You might be interested into decoding events emitted by a contract. You can do so by using the `TransactionEventsParser`. + +// Suppose we'd like to decode a `startPerformAction` event emitted by the [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract. + +// First, we load the abi file, then we fetch the transaction, we extract the event from the transaction and then we parse it. + +// ```js +{ + // load the abi files + const entrypoint = new DevnetEntrypoint(); + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + const parser = new TransactionEventsParser({ abi }); + const transactionOnNetwork = entrypoint.getTransaction(txHash); + const events = gatherAllEvents(transactionOnNetwork); + const outcome = parser.parseEvents({ events }); +} +// ``` + +// #### Decoding transaction events +// Whenever needed, the contract ABI can be used for manually encoding or decoding custom types. + +// Let's encode a struct called EsdtTokenPayment (of [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract) into binary data. +// ```js +{ + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + const paymentType = abi.getStruct("EsdtTokenPayment"); + + const paymentStruct = new Struct(paymentType, [ + new Field(new TokenIdentifierValue("TEST-8b028f"), "token_identifier"), + new Field(new U64Value(0n), "token_nonce"), + new Field(new BigUIntValue(10000n), "amount") + ]); + + const encoded = codec.encodeNested(paymentStruct); + + console.log(encoded.toString("hex")); +} +// ``` + +// Now let's decode a struct using the ABI. +// ```js +{ + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + const actionStructType = abi.getEnum("Action"); + const data = Buffer.from("0500000000000000000500d006f73c4221216fa679bc559005584c4f1160e569e1000000012a0000000003616464000000010000000107", "hex"); + + const [decoded] = codec.decodeNested(data, actionStructType); + const decodedValue = decoded.valueOf(); + console.log(JSON.stringify(decodedValue, null, 4)); +} +// ``` + +// ### Smart Contract queries +// When querying a smart contract, a **view function** is called. A view function does not modify the state of the contract, so we do not need to send a transaction. +// To perform this query, we use the **SmartContractController**. While we can use the contract's ABI file to encode the query arguments, we can also use it to parse the result. +// In this example, we will query the **adder smart contract** by calling its `getSum` endpoint. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + + // create the controller + const controller = entrypoint.createSmartContractController(abi); + + // creates the query, runs the query, parses the result + const response = await controller.query({ contract: contractAddress, function: "getSum", arguments: [] }); +} +// ``` + +// If we need more granular control, we can split the process into three steps: **create the query, run the query, and parse the query response**. +// This approach achieves the same result as the previous example. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + + // load the abi + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + + // the contract address we'll query + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + // create the controller + const controller = entrypoint.createSmartContractController(abi); + + // create the query + const query = await controller.createQuery({ contract: contractAddress, function: "getSum", arguments: [] }); + // runs the query + const response = await controller.runQuery(query); + + // parse the result + const parsedResponse = controller.parseQueryResponse(response); +} +// ``` + +// ### Upgrading a smart contract +// Contract upgrade transactions are similar to deployment transactions (see above) because they also require contract bytecode. +// However, in this case, the contract address is already known. Like deploying a smart contract, we can upgrade a smart contract using either the **controller** or the **factory**. + +// #### Uprgrading a smart contract using the controller +// ```js +{ + // prepare the account + const entrypoint = new DevnetEntrypoint(); + const keystorePath = path.join("src", "testdata", "testwallets", "alice.json"); + const account = Account.newFromKeystore({ + filePath: keystorePath, + password: "password" + }); + // the developer is responsible for managing the nonce + account.nonce = entrypoint.recall_account_nonce(account.address); + + // load the abi + const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + + // create the controller + const controller = entrypoint.createSmartContractController(abi); + + // load the contract bytecode; this is the new contract code, the one we want to upgrade to + const bytecode = await promises.readFile("../contracts/adder.wasm"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment + let args = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment + args = [42]; + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + const upgradeTransaction = await controller.createTransactionForUpgrade( + sender, + sender.getNonceThenIncrement(), + { + contract: contractAddress, + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }, + ); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(upgradeTransaction); + + console.log({ txHash }); +} +// ``` \ No newline at end of file diff --git a/cookbook/tokens.js b/cookbook/tokens.js new file mode 100644 index 00000000..776ee587 --- /dev/null +++ b/cookbook/tokens.js @@ -0,0 +1,417 @@ +import { Account, Address, DevnetEntrypoint, TokenManagementTransactionsOutcomeParser } from "@multiversx/sdk-core"; // md-ignore +import path from 'path'; // md-ignore +// ### Token management + +// In this section, we're going to create transactions to issue fungible tokens, issue semi-fungible tokens, create NFTs, set token roles, but also parse these transactions to extract their outcome (e.g. get the token identifier of the newly issued token). + +// These methods are available through the `TokenManagementController` and the `TokenManagementTransactionsFactory`. +// The controller also includes built-in methods for awaiting the completion of transactions and parsing their outcomes. +// For the factory, the same functionality can be achieved using the `TokenManagementTransactionsOutcomeParser`. + +// For scripts or quick network interactions, we recommend using the controller. However, for a more granular approach (e.g., DApps), the factory is the better choice. + +// #### Issuing fungible tokens using the controller +// ```js +{ + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }, + ); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + const outcome = await entrypoint.awaitCompletedIssueFungible(txHash); + + const tokenIdentifier = outcome[0].tokenIdentifier; + +} +// ``` + +// #### Issuing fungible tokens using the factory +// ```js +{ + // create the entrypoint and the token management transactions factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForIssuingFungible( + alice, + { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }, + ); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` // md-as-comment + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier // md-as-comment + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueFungible(transactionOnNetwork); + const tokenIdentifier = outcome[0].tokenIdentifier; +} +// ``` + + +// #### Setting special roles for fungible tokens using the controller +// ```js +{ + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await controller.createTransactionForSettingSpecialRoleOnFungibleToken( + alice, + alice.getNonceThenIncrement(), + { + user: bob, + tokenIdentifier: "TEST-123456", + addRoleLocalMint: true, + addRoleLocalBurn: true, + addRoleESDTTransferRole: true, + }, + ); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + const outcome = await entrypoint.awaitCompletedSetSpecialRoleOnFungibleToken(transaction); + + const roles = outcome[0].roles; + const user = outcome[0].userAddress; +} +// ``` + +// #### Setting special roles for fungible tokens using the factory +// ```js +{ + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await factory.createTransactionForIssuingFungible( + alice, + { + user: bob, + tokenIdentifier: "TEST-123456", + addRoleLocalMint: true, + addRoleLocalBurn: true, + addRoleESDTTransferRole: true, + }, + ); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` // md-as-comment + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseSetSpecialRole(transactionOnNetwork); + + const roles = outcome[0].roles; + const user = outcome[0].userAddress; +} +// ``` + +// #### Issuing semi-fungible tokens using the controller +// ```js +{ + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingSemiFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWSEMI", + tokenTicker: "SEMI", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }, + ); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + const outcome = await entrypoint.awaitCompletedIssueSemiFungible(txHash); + + const tokenIdentifier = outcome[0].tokenIdentifier; +} +// ``` + +// #### Issuing semi-fungible tokens using the factory +// ```js +{ + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForIssuingSemiFungible( + alice, + { + tokenName: "NEWSEMI", + tokenTicker: "SEMI", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }, + ); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier // md-as-comment + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueSemiFungible(transactionOnNetwork); + + const tokenIdentifier = outcome[0].tokenIdentifier; +} +// ``` + +// #### Issuing NFT collection & creating NFTs using the controller + +// ```js +{ + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.creatTokenManagementController(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + let transaction = await controller.createTransactionForIssuingNonFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWNFT", + tokenTicker: "NFT", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }, + ); + + // sending the transaction // md-as-comment + let txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + let outcome = await entrypoint.awaitCompletedIssueNonFungible(txHash); + + const collectionIdentifier = outcome[0].tokenIdentifier; + + // create an NFT // md-as-comment + transaction = controller.createTransactionForCreatingNft(alice, + alice.getNonceThenIncrement(), + { + tokenIdentifier: "FRANK-aa9e8d", + initialQuantity: 1n, + name: "test", + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + }, + ); + + // sending the transaction // md-as-comment + txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + outcome = await entrypoint.awaitCompletedCreateNft(txHash); + + const identifier = outcome[0].tokenIdentifier; + const nonce = outcome[0].nonce; + const initialQuantity = outcome[0].initialQuantity; + +} +// ``` + +// #### Issuing NFT collection & creating NFTs using the factory +// ```js +{ + // create the entrypoint and the token management transdactions factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + let transaction = await factory.createTransactionForIssuingNonFungible( + alice, + { + tokenName: "NEWNFT", + tokenTicker: "NFT", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }, + ); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + let txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + let transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier // md-as-comment + let parser = new TokenManagementTransactionsOutcomeParser(); + let outcome = parser.parseIssueNonFungible(transactionOnNetwork); + + const collectionIdentifier = outcome[0].tokenIdentifier; + + transaction = await factory.createTransactionForCreatingNFT( + alice, + { + tokenIdentifier: "FRANK-aa9e8d", + initialQuantity: 1n, + name: "test", + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + }, + ); + + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + txHash = await entrypoint.sendTransaction(transaction); + + // ### wait for transaction to execute, extract the token identifier // md-as-comment + transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + outcome = parser.parseIssueNonFungible(transactionOnNetwork); + + const identifier = outcome[0].tokenIdentifier; + const nonce = outcome[0].nonce; + const initialQuantity = outcome[0].initialQuantity; +} +// ``` + +// These are just a few examples of what you can do using the token management controller or factory. For a complete list of supported methods, please refer to the autogenerated documentation: + +// - [TokenManagementController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementController.html) +// - [TokenManagementTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementTransactionsFactory.html) \ No newline at end of file diff --git a/cookbook/transactions.js b/cookbook/transactions.js new file mode 100644 index 00000000..a7d014da --- /dev/null +++ b/cookbook/transactions.js @@ -0,0 +1,205 @@ + +import { Account, DevnetEntrypoint, Token, TokenTransfer, TransactionsFactoryConfig, TransfersController, TransferTransactionsFactory } from '@multiversx/sdk-core'; // md-ignore +import path from 'path'; // md-ignore +// ## Creating transactions + +// In this section, we’ll explore how to create different types of transactions. To create transactions, we can use either controllers or factories. +// Controllers are ideal for quick scripts or network interactions, while factories provide a more granular and lower-level approach, typically required for DApps. + +// Controllers typically use the same parameters as factories, but they also require an Account object and the sender’s nonce. +// Controllers also include extra functionality, such as waiting for transaction completion and parsing transactions. +// The same functionality can be achieved for transactions built using factories, and we’ll see how in the sections below. In the next section, we’ll learn how to create transactions using both methods. + +// ### Instantiating Controllers and Factories +// There are two ways to create controllers and factories: +// 1. Get them from the entrypoint. +// 2. Manually instantiate them. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + + // getting the controller and the factory from the entrypoint + const transfersController = entrypoint.createTransfersController(); + const transfersFactory = entrypoint.createTransfersTransactionsFactory(); + + // manually instantiating the controller and the factory + const controller = new TransfersController({ chainID: 'D' }); + + const config = new TransactionsFactoryConfig({ chainID: 'D' }); + const factory = new TransferTransactionsFactory({ config }); +} +// ``` + +// ### Token transfers +// We can send both native tokens (EGLD) and ESDT tokens using either the controller or the factory. +// #### Native Token Transfers Using the Controller +// When using the controller, the transaction will be signed because we’ll be working with an Account. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transfersController = entrypoint.createTransfersController(); + const transaction = await transfersController.createTransactionForTransfer( + alice, + alice.getNonceThenIncrement(), + { + receiver: bob, + nativeAmount: 1n, + }, + ); + + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. + +// #### Native Token Transfers Using the Factory +// When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. +// You will need to handle these aspects after the transaction is created. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTransfersTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = factory.createTransactionForTransfer(alice, { + receiver: bob, + nativeAmount: 1000000000000000000n, + }); + + // set the sender's nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction using the sender's account + transaction.signature = await alice.signTransaction(transaction); + + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. + +// #### Custom token transfers using the controller + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const esdt = new Token({ identifier: "TEST-123456" }); + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); + + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); + + const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); + const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); + + const transfersController = entrypoint.createTransfersController(); + const transaction = transfersController.createTransactionForTransfer(alice, alice.getNonceThenIncrement(), { + receiver: bob, + tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], + }); + + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. + +// #### Custom token transfers using the factory +// When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. These aspects should be handled after the transaction is created. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTransfersTransactionsFactory(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const esdt = new Token({ identifier: "TEST-123456" }); // fungible tokens don't have a nonce + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); // we set the desired amount we want to send + + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); // for NFTs we set the amount to `1` + + const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); + const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); // for SFTs we set the desired amount we want to send + + const transaction = factory.createTransactionForTransfer(alice, { + receiver: bob, + tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], + }); + + // set the sender's nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction using the sender's account + transaction.signature = await alice.signTransaction(transaction); + + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` + +// If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. + +// #### Sending native and custom tokens +// Both native and custom tokens can now be sent. If a `nativeAmount` is provided along with `tokenTransfers`, the native token will be included in the `MultiESDTNFTTransfer` built-in function call. +// We can send both types of tokens using either the `controller` or the `factory`, but for simplicity, we’ll use the controller in this example. + +// ```js +{ + const entrypoint = new DevnetEntrypoint(); + + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const esdt = new Token({ identifier: "TEST-123456" }); + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); + + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); + + const transfersController = entrypoint.createTransfersController(); + const transaction = transfersController.createTransactionForTransfer(alice, alice.getNonceThenIncrement(), { + receiver: bob, + nativeAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer], + }); + + const txHash = await entrypoint.sendTransaction(transaction); +} +// ``` diff --git a/cookbook/verifySIgnatures.js b/cookbook/verifySIgnatures.js new file mode 100644 index 00000000..808f5cd3 --- /dev/null +++ b/cookbook/verifySIgnatures.js @@ -0,0 +1,147 @@ + +import { Account, Address, Message, MessageComputer, Transaction, TransactionComputer, UserPublicKey, UserVerifier } from "@multiversx/sdk-core"; // md-ignore +import path from 'path'; // md-ignore +// ## Verifying signatures + +// Signature verification is performed using an account’s public key. +// To simplify this process, we provide wrappers over public keys that make verification easier and more convenient. + +// #### Verifying Transaction signature using a UserVerifier + +// ```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + nonce: 90n, + sender: account.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D" + }); + + // sign and apply the signature on the transaction // md-as-comment + transaction.signature = await account.sign(transaction); + + // instantiating a user verifier; basically gets the public key // md-as-comment + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const aliceVerifier = UserVerifier.fromAddress(alice); + + // serialize the transaction for verification // md-as-comment + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); + + // verify the signature // md-as-comment + const isSignedByAlice = aliceVerifier.verify(serializedTransaction, transaction.signature); + + + console.log("Transaction is signed by Alice: ", isSignedByAlice); +} +// ``` + +// #### Verifying Message signature using a UserVerifier + +// ```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); + + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: account.address + }); + + // sign and apply the signature on the message // md-as-comment + message.signature = await account.sign(message); + + // instantiating a user verifier; basically gets the public key // md-as-comment + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const aliceVerifier = UserVerifier.fromAddress(alice); + + // serialize the message for verification // md-as-comment + const messageComputer = new MessageComputer(); + const serializedMessage = messageComputer.computeBytesForVerifying(message); + + // verify the signature // md-as-comment + const isSignedByAlice = aliceVerifier.verify(serializedMessage, message.signature); + + console.log("Message is signed by Alice: ", isSignedByAlice); +} +// ``` + +// #### Verifying a signature using a public key +// ```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + nonce: 90n, + sender: account.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D" + }); + + // sign and apply the signature on the transaction // md-as-comment + transaction.signature = await account.sign(transaction); + + // instantiating a public key // md-as-comment + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const publicKey = new UserPublicKey(alice.getPublicKey()); + + // serialize the transaction for verification // md-as-comment + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); + + // verify the signature // md-as-comment + const isSignedByAlice = publicKey.verify(serializedTransaction, transaction.signature); + console.log("Transaction is signed by Alice: ", isSignedByAlice); +} +// ``` + +// #### Sending messages over boundaries +// Signed Message objects are typically sent to a remote party (e.g., a service), which can then verify the signature. +// To prepare a message for transmission, you can use the `MessageComputer.packMessage()` utility method. + +// ```js +{ + const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); + + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: account.address + }); + + // sign and apply the signature on the message // md-as-comment + message.signature = await account.sign(message); + + const messageComputer = new MessageComputer(); + const packedMessage = messageComputer.packMessage(message); + + console.log("Packed message", packedMessage); +} +// ``` + +// Then, on the receiving side, you can use `func:MessageComputer.unpackMessage()` to reconstruct the message, prior verification: + +// ```js +{ + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + + const messageComputer = new MessageComputer(); + + // restore message // md-as-comment + const message = messageComputer.unpackMessage(packedMessage); + + // verify the signature // md-as-comment + const publicKey = new UserPublicKey(alice.getPublicKey()); + const isSignedByAlice = publicKey.verify(messageComputer.computeBytesForVerifying(message), message.signature); + + console.log("Transaction is signed by Alice: ", isSignedByAlice); +} +// ``` diff --git a/cookbook/wallets.js b/cookbook/wallets.js new file mode 100644 index 00000000..043ff66b --- /dev/null +++ b/cookbook/wallets.js @@ -0,0 +1,141 @@ + +import { KeyPair, Mnemonic, UserPem, UserWallet } from "@multiversx/sdk-core"; // md-ignore +import path from 'path'; // md-ignore +// ## Wallets + +// #### Generating a mnemonic +// Mnemonic generation is based on [bip39](https://www.npmjs.com/package/bip39) and can be achieved as follows: + +// ``` js + +const mnemonic = Mnemonic.generate(); +const words = mnemonic.getWords(); +console.log({ words }); +// ``` + +// #### Saving the mnemonic to a keystore file +// The mnemonic can be saved to a keystore file: + +// ``` js +{ + const mnemonic = Mnemonic.generate(); + + // saves the mnemonic to a keystore file with kind=mnemonic // md-as-comment + const wallet = UserWallet.fromMnemonic({ mnemonic: mnemonic.getText(), password: "password" }); + + const filePath = path.join("src", "testdata", "testwallets", "walletWithMnemonic.json"); + wallet.save(filePath); +} +// ``` + +// #### Deriving secret keys from a mnemonic +// Given a mnemonic, we can derive keypairs: + +// ``` js +{ + const mnemonic = Mnemonic.generate(); + + const secretKey = mnemonic.deriveKey(0); + const publicKey = secretKey.generatePublicKey(); + + console.log("Secret key: ", secretKey.hex()); + console.log("Public key: ", publicKey.hex()); +} +// ``` + +// #### Saving a secret key to a keystore file +// The secret key can also be saved to a keystore file: + +// ``` js +{ + const mnemonic = Mnemonic.generate(); + const secretKey = mnemonic.deriveKey(); + + const wallet = UserWallet.fromSecretKey({ secretKey: secretKey, password: "password" }); + + const filePath = path.join("src", "testdata", "testwallets", "walletWithSecretKey.json"); + wallet.save(filePath); +} +// ``` + +// #### Saving a secret key to a PEM file +// We can save a secret key to a pem file. *This is not recommended as it is not secure, but it's very convenient for testing purposes.* + +// ``` js +{ + const mnemonic = Mnemonic.generate(); + + // by default, derives using the index = 0 // md-as-comment + const secretKey = mnemonic.deriveKey(); + const publicKey = secretKey.generatePublicKey(); + + const label = publicKey.toAddress().toBech32(); + const pem = new UserPem(label, secretKey); + + const filePath = path.join("src", "testdata", "testwallets", "wallet.pem"); + pem.save(filePath); +} +// ``` + +// #### Generating a KeyPair +// A `KeyPair` is a wrapper over a secret key and a public key. We can create a keypair and use it for signing or verifying. + +// ``` js +{ + const keypair = KeyPair.generate(); + + // by default, derives using the index = 0 // md-as-comment + const secretKey = keypair.getSecretKey(); + const publicKey = keypair.getPublicKey(); +} +// ``` + +// #### Loading a wallet from keystore mnemonic file +// Load a keystore that holds an encrypted mnemonic (and perform wallet derivation at the same time): + +// ``` js +{ + const filePath = path.join("src", "testdata", "testwallets", "walletWithMnemonic.json"); + + // loads the mnemonic and derives the a secret key; default index = 0 // md-as-comment + let secretKey = UserWallet.loadSecretKey(filePath, "password"); + let address = secretKey.generatePublicKey().toAddress('erd'); + + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); + + // derive secret key with index = 7 // md-as-comment + secretKey = UserWallet.loadSecretKey(path, "password", 7); + address = secretKey.generatePublicKey().toAddress(); + + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); +} +// ``` + +// #### Loading a wallet from a keystore secret key file + +// ``` js +{ + const filePath = path.join("src", "testdata", "testwallets", "walletWithSecretKey.json"); + + let secretKey = UserWallet.loadSecretKey(filePath, "password"); + let address = secretKey.generatePublicKey().toAddress('erd'); + + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); +} +// ``` + +// #### Loading a wallet from a PEM file + +// ``` js +{ + const filePath = path.join("src", "testdata", "testwallets", "wallet.pem"); + + let pem = UserPem.fromFile(filePath); + + console.log("Secret key: ", pem.secretKey.hex()); + console.log("Public key: ", pem.publicKey.hex()); +} +// ``` \ No newline at end of file From bc37e2608823f4d6fee0269f2192d1ae2399c7c8 Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 16 Apr 2025 16:23:30 +0300 Subject: [PATCH 02/15] Remove package json as not needed --- cookbook/package-lock.json | 4620 ------------------------------------ cookbook/package.json | 9 - 2 files changed, 4629 deletions(-) delete mode 100644 cookbook/package-lock.json delete mode 100644 cookbook/package.json diff --git a/cookbook/package-lock.json b/cookbook/package-lock.json deleted file mode 100644 index 8d2d72d0..00000000 --- a/cookbook/package-lock.json +++ /dev/null @@ -1,4620 +0,0 @@ -{ - "name": "cookbook", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "cookbook", - "dependencies": { - "@multiversx/sdk-core": "14.0.0", - "@multiversx/sdk-dapp": "2.40.11", - "axios": "1.7.7", - "chai": "5.1.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", - "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@ethereumjs/common": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-3.2.0.tgz", - "integrity": "sha512-pksvzI0VyLgmuEF2FA/JR/4/y6hcPq8OUail3/AvycBaW1d5VSauOZzqGvJ3RTmR4MU35lWE8KseKOsEhrFRBA==", - "license": "MIT", - "dependencies": { - "@ethereumjs/util": "^8.1.0", - "crc-32": "^1.2.0" - } - }, - "node_modules/@ethereumjs/rlp": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", - "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", - "license": "MPL-2.0", - "bin": { - "rlp": "bin/rlp" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@ethereumjs/tx": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-4.2.0.tgz", - "integrity": "sha512-1nc6VO4jtFd172BbSnTnDQVr9IYBFl1y4xPzZdtkrkKIncBCkdbgfdRV+MiTkJYAtTxvV12GRZLqBFT1PNK6Yw==", - "license": "MPL-2.0", - "dependencies": { - "@ethereumjs/common": "^3.2.0", - "@ethereumjs/rlp": "^4.0.1", - "@ethereumjs/util": "^8.1.0", - "ethereum-cryptography": "^2.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@ethereumjs/util": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", - "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", - "license": "MPL-2.0", - "dependencies": { - "@ethereumjs/rlp": "^4.0.1", - "ethereum-cryptography": "^2.0.0", - "micro-ftch": "^0.3.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz", - "integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz", - "integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.5.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz", - "integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==", - "hasInstallScript": true, - "license": "(CC-BY-4.0 AND MIT)", - "optional": true, - "dependencies": { - "@fortawesome/fontawesome-common-types": "6.5.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@fortawesome/react-fontawesome": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz", - "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==", - "license": "MIT", - "optional": true, - "dependencies": { - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "~1 || ~6", - "react": ">=16.3" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@ledgerhq/devices": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.0.3.tgz", - "integrity": "sha512-Q7/vqkGELSBuwFafFoTqlHIRyOjw8JqbSgiQwe2R38xN0RKtKIh+5E6UfMKyoExiq+SrQg0IC8P2LS+XdjOHLw==", - "license": "Apache-2.0", - "dependencies": { - "@ledgerhq/errors": "^6.12.6", - "@ledgerhq/logs": "^6.10.1", - "rxjs": "6", - "semver": "^7.3.5" - } - }, - "node_modules/@ledgerhq/errors": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.12.6.tgz", - "integrity": "sha512-D+r2B09vaRO06wfGoss+rNgwqWSoK0bCtsaJWzlD2hv1zxTtucqVtSztbRFypIqxWTCb3ix5Nh2dWHEJVTp2Xw==", - "license": "Apache-2.0" - }, - "node_modules/@ledgerhq/hw-transport": { - "version": "6.28.4", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.28.4.tgz", - "integrity": "sha512-fB2H92YQjidmae2GFCmOGPwkZWk0lvTu0tlLlzfiY0wRheAG+DEgjnqhdU8wmydkPLIj0WUjRgldtnJtg/a2iQ==", - "license": "Apache-2.0", - "dependencies": { - "@ledgerhq/devices": "^8.0.3", - "@ledgerhq/errors": "^6.12.6", - "events": "^3.3.0" - } - }, - "node_modules/@ledgerhq/hw-transport-web-ble": { - "version": "6.27.17", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-web-ble/-/hw-transport-web-ble-6.27.17.tgz", - "integrity": "sha512-OoKbImhgyi43F6PM2KAJfViW+oEmZ3M8Tq9XZ0X57oL6QCQUYv6FakEFFkZzNuNWFdtFKGPgNt6xygqIdNElEQ==", - "license": "Apache-2.0", - "dependencies": { - "@ledgerhq/devices": "^8.0.5", - "@ledgerhq/errors": "^6.13.0", - "@ledgerhq/hw-transport": "^6.28.6", - "@ledgerhq/logs": "^6.10.1", - "rxjs": "6" - } - }, - "node_modules/@ledgerhq/hw-transport-web-ble/node_modules/@ledgerhq/devices": { - "version": "8.4.4", - "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.4.4.tgz", - "integrity": "sha512-sz/ryhe/R687RHtevIE9RlKaV8kkKykUV4k29e7GAVwzHX1gqG+O75cu1NCJUHLbp3eABV5FdvZejqRUlLis9A==", - "license": "Apache-2.0", - "dependencies": { - "@ledgerhq/errors": "^6.19.1", - "@ledgerhq/logs": "^6.12.0", - "rxjs": "^7.8.1", - "semver": "^7.3.5" - } - }, - "node_modules/@ledgerhq/hw-transport-web-ble/node_modules/@ledgerhq/devices/node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@ledgerhq/hw-transport-web-ble/node_modules/@ledgerhq/errors": { - "version": "6.19.1", - "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.19.1.tgz", - "integrity": "sha512-75yK7Nnit/Gp7gdrJAz0ipp31CCgncRp+evWt6QawQEtQKYEDfGo10QywgrrBBixeRxwnMy1DP6g2oCWRf1bjw==", - "license": "Apache-2.0" - }, - "node_modules/@ledgerhq/hw-transport-web-ble/node_modules/@ledgerhq/hw-transport": { - "version": "6.31.4", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.31.4.tgz", - "integrity": "sha512-6c1ir/cXWJm5dCWdq55NPgCJ3UuKuuxRvf//Xs36Bq9BwkV2YaRQhZITAkads83l07NAdR16hkTWqqpwFMaI6A==", - "license": "Apache-2.0", - "dependencies": { - "@ledgerhq/devices": "^8.4.4", - "@ledgerhq/errors": "^6.19.1", - "@ledgerhq/logs": "^6.12.0", - "events": "^3.3.0" - } - }, - "node_modules/@ledgerhq/hw-transport-web-ble/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@ledgerhq/hw-transport-webhid": { - "version": "6.27.15", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-webhid/-/hw-transport-webhid-6.27.15.tgz", - "integrity": "sha512-xhtFy/SNttvBna8t1ZiP74K6Lj8uDhp0W+Zjvwz4IS6tS9gWZbKUB7scbrNrJep6Q77Of1bzDXrswyaoxFZrUg==", - "license": "Apache-2.0", - "dependencies": { - "@ledgerhq/devices": "^8.0.3", - "@ledgerhq/errors": "^6.12.6", - "@ledgerhq/hw-transport": "^6.28.4", - "@ledgerhq/logs": "^6.10.1" - } - }, - "node_modules/@ledgerhq/hw-transport-webusb": { - "version": "6.27.15", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport-webusb/-/hw-transport-webusb-6.27.15.tgz", - "integrity": "sha512-XRteQmJMFbMETOwwwJrXhEvBEdu1DpuDQNI04bzMQE9p81rLjwd6pIhfP/W1O94av7Iq4kaatNsNpxkitmp59w==", - "license": "Apache-2.0", - "dependencies": { - "@ledgerhq/devices": "^8.0.3", - "@ledgerhq/errors": "^6.12.6", - "@ledgerhq/hw-transport": "^6.28.4", - "@ledgerhq/logs": "^6.10.1" - } - }, - "node_modules/@ledgerhq/logs": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@ledgerhq/logs/-/logs-6.12.0.tgz", - "integrity": "sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA==", - "license": "Apache-2.0" - }, - "node_modules/@lifeomic/axios-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@lifeomic/axios-fetch/-/axios-fetch-3.0.1.tgz", - "integrity": "sha512-bwEgYXtGrn/F+yYqoUIAWBRzyqQ7yB1VL84gCq0uAk36GRoyoWyOzbE35VWeXlmkkoby91FnAwh4UhgayMM/og==", - "license": "MIT", - "dependencies": { - "@types/node-fetch": "^2.5.10" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@metamask/json-rpc-engine": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/@metamask/json-rpc-engine/-/json-rpc-engine-7.3.3.tgz", - "integrity": "sha512-dwZPq8wx9yV3IX2caLi9q9xZBw2XeIoYqdyihDDDpuHVCEiqadJLwqM3zy+uwf6F1QYQ65A8aOMQg1Uw7LMLNg==", - "license": "ISC", - "dependencies": { - "@metamask/rpc-errors": "^6.2.1", - "@metamask/safe-event-emitter": "^3.0.0", - "@metamask/utils": "^8.3.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@metamask/json-rpc-middleware-stream": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@metamask/json-rpc-middleware-stream/-/json-rpc-middleware-stream-6.0.2.tgz", - "integrity": "sha512-jtyx3PRfc1kqoLpYveIVQNwsxYKefc64/LCl9h9Da1m3nUKEvypbYuXSIwi237qvOjKmNHQKsDOZg6f4uBf62Q==", - "license": "ISC", - "dependencies": { - "@metamask/json-rpc-engine": "^7.3.2", - "@metamask/safe-event-emitter": "^3.0.0", - "@metamask/utils": "^8.3.0", - "readable-stream": "^3.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@metamask/object-multiplex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@metamask/object-multiplex/-/object-multiplex-2.1.0.tgz", - "integrity": "sha512-4vKIiv0DQxljcXwfpnbsXcfa5glMj5Zg9mqn4xpIWqkv6uJ2ma5/GtUfLFSxhlxnR8asRMv8dDmWya1Tc1sDFA==", - "license": "ISC", - "dependencies": { - "once": "^1.4.0", - "readable-stream": "^3.6.2" - }, - "engines": { - "node": "^16.20 || ^18.16 || >=20" - } - }, - "node_modules/@metamask/providers": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@metamask/providers/-/providers-16.0.0.tgz", - "integrity": "sha512-zqZpn7Tg+QuvhL9x1WYCdXjFclZrMR2LOU2a3kHVFms5pOfizzFApB492VFgHuv/5xKohSxDkhXpAZ/TNGmM4g==", - "license": "MIT", - "dependencies": { - "@metamask/json-rpc-engine": "^7.3.2", - "@metamask/json-rpc-middleware-stream": "^6.0.2", - "@metamask/object-multiplex": "^2.0.0", - "@metamask/rpc-errors": "^6.2.1", - "@metamask/safe-event-emitter": "^3.0.0", - "@metamask/utils": "^8.3.0", - "detect-browser": "^5.2.0", - "extension-port-stream": "^3.0.0", - "fast-deep-equal": "^3.1.3", - "is-stream": "^2.0.0", - "readable-stream": "^3.6.2", - "webextension-polyfill": "^0.10.0" - }, - "engines": { - "node": "^18.18 || >=20" - } - }, - "node_modules/@metamask/rpc-errors": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@metamask/rpc-errors/-/rpc-errors-6.4.0.tgz", - "integrity": "sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==", - "license": "MIT", - "dependencies": { - "@metamask/utils": "^9.0.0", - "fast-safe-stringify": "^2.0.6" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@metamask/rpc-errors/node_modules/@metamask/utils": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-9.3.0.tgz", - "integrity": "sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==", - "license": "ISC", - "dependencies": { - "@ethereumjs/tx": "^4.2.0", - "@metamask/superstruct": "^3.1.0", - "@noble/hashes": "^1.3.1", - "@scure/base": "^1.1.3", - "@types/debug": "^4.1.7", - "debug": "^4.3.4", - "pony-cause": "^2.1.10", - "semver": "^7.5.4", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@metamask/rpc-errors/node_modules/@noble/hashes": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", - "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@metamask/rpc-errors/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@metamask/safe-event-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@metamask/safe-event-emitter/-/safe-event-emitter-3.1.2.tgz", - "integrity": "sha512-5yb2gMI1BDm0JybZezeoX/3XhPDOtTbcFvpTXM9kxsoZjPZFh4XciqRbpD6N86HYZqWDhEaKUDuOyR0sQHEjMA==", - "license": "ISC", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@metamask/superstruct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@metamask/superstruct/-/superstruct-3.1.0.tgz", - "integrity": "sha512-N08M56HdOgBfRKkrgCMZvQppkZGcArEop3kixNEtVbJKm6P9Cfg0YkI6X0s1g78sNrj2fWUwvJADdZuzJgFttA==", - "license": "MIT", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@metamask/utils": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@metamask/utils/-/utils-8.5.0.tgz", - "integrity": "sha512-I6bkduevXb72TIM9q2LRO63JSsF9EXduh3sBr9oybNX2hNNpr/j1tEjXrsG0Uabm4MJ1xkGAQEMwifvKZIkyxQ==", - "license": "ISC", - "dependencies": { - "@ethereumjs/tx": "^4.2.0", - "@metamask/superstruct": "^3.0.0", - "@noble/hashes": "^1.3.1", - "@scure/base": "^1.1.3", - "@types/debug": "^4.1.7", - "debug": "^4.3.4", - "pony-cause": "^2.1.10", - "semver": "^7.5.4", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@metamask/utils/node_modules/@noble/hashes": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", - "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@metamask/utils/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@multiversx/sdk-bls-wasm": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-bls-wasm/-/sdk-bls-wasm-0.3.5.tgz", - "integrity": "sha512-c0tIdQUnbBLSt6NYU+OpeGPYdL0+GV547HeHT8Xc0BKQ7Cj0v82QUoA2QRtWrR1G4MNZmLsIacZSsf6DrIS2Bw==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/@multiversx/sdk-core": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-core/-/sdk-core-14.0.0.tgz", - "integrity": "sha512-cW1KViTPUv/VhoR0YrZT7zuTp2YOWmPxIay6yR0oOuOAWytOpUkwdDgAGKLsbonKq3yfehA+uK4yhazICJi2aA==", - "license": "MIT", - "dependencies": { - "@multiversx/sdk-transaction-decoder": "1.0.2", - "@noble/ed25519": "1.7.3", - "@noble/hashes": "1.3.0", - "bech32": "1.1.4", - "blake2b": "2.1.3", - "buffer": "6.0.3", - "ed25519-hd-key": "1.1.2", - "ed2curve": "0.3.0", - "json-bigint": "1.0.0", - "keccak": "3.0.2", - "scryptsy": "2.1.0", - "tweetnacl": "1.0.3", - "uuid": "8.3.2" - }, - "optionalDependencies": { - "@multiversx/sdk-bls-wasm": "0.3.5", - "axios": "^1.7.4", - "bip39": "3.1.0" - }, - "peerDependencies": { - "bignumber.js": "^9.0.1", - "protobufjs": "^7.2.6" - } - }, - "node_modules/@multiversx/sdk-dapp": { - "version": "2.40.11", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-dapp/-/sdk-dapp-2.40.11.tgz", - "integrity": "sha512-TBvZpvWDAWHoEw5ydDKQExVjSB54+jkzxWxZdttmLHKHK9W8Bk9dAbfD6CrN1AgKvbk3lz9JMCdh7mtqQYG63w==", - "license": "GPL-3.0-or-later", - "dependencies": { - "@lifeomic/axios-fetch": "3.0.1", - "@metamask/providers": "16.0.0", - "@multiversx/sdk-dapp-utils": "0.1.0", - "@multiversx/sdk-extension-provider": "3.0.0", - "@multiversx/sdk-hw-provider": "6.4.0", - "@multiversx/sdk-metamask-provider": "0.0.7", - "@multiversx/sdk-native-auth-client": "1.0.9", - "@multiversx/sdk-opera-provider": "1.0.0-alpha.1", - "@multiversx/sdk-passkey-provider": "1.0.9", - "@multiversx/sdk-wallet": "4.6.0", - "@multiversx/sdk-wallet-connect-provider": "4.1.4", - "@multiversx/sdk-web-wallet-cross-window-provider": "1.0.0", - "@multiversx/sdk-web-wallet-iframe-provider": "1.0.3", - "@multiversx/sdk-web-wallet-provider": "3.2.1", - "@multiversx/sdk-webview-provider": "1.0.0", - "@reduxjs/toolkit": "1.8.2", - "bignumber.js": "9.x", - "linkify-react": "4.0.2", - "linkifyjs": "4.0.2", - "lodash.debounce": "4.0.8", - "lodash.isequal": "4.5.0", - "lodash.omit": "4.5.0", - "lodash.throttle": "4.1.1", - "lodash.uniqby": "4.7.0", - "protobufjs": "7.2.6", - "qs": "6.10.3", - "react-idle-timer": "5.0.0", - "react-redux": "8.0.2", - "redux-persist": "6.0.0", - "reselect": "4.0.0", - "shx": "0.3.4", - "socket.io-client": "4.6.1" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@fortawesome/fontawesome-svg-core": "6.5.1", - "@fortawesome/free-solid-svg-icons": "6.5.1", - "@fortawesome/react-fontawesome": "0.2.0", - "classnames": "2.3.1", - "platform": "1.3.6", - "qrcode": "1.5.0", - "swr": "2.2.0" - }, - "peerDependencies": { - "@multiversx/sdk-core": ">= 12.18.0", - "@types/react": "^18.0.24", - "@types/react-dom": "^18.0.8", - "axios": ">=1.7.4", - "react": "^18.2.0", - "react-dom": "^18.2.0" - } - }, - "node_modules/@multiversx/sdk-dapp-utils": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-dapp-utils/-/sdk-dapp-utils-0.1.0.tgz", - "integrity": "sha512-EFvktZ/S1WQ1ie02nnKZHARC4r23JZWwoTFd5py1qi/Z/UoLHIzJ394HLjXFb6gBTsp4wnvNwIXBA/DNrd2Yeg==", - "license": "GPL-3.0-or-later", - "peerDependencies": { - "@multiversx/sdk-core": ">= 12.1.0" - } - }, - "node_modules/@multiversx/sdk-dapp/node_modules/protobufjs": { - "version": "7.2.6", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", - "integrity": "sha512-dgJaEDDL6x8ASUZ1YqWciTRrdOuYNzoOf27oHNfdyvKqHr5i0FV7FSLU+aIeFjyFgVxrpTOtQUi0BLLBymZaBw==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@multiversx/sdk-extension-provider": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-extension-provider/-/sdk-extension-provider-3.0.0.tgz", - "integrity": "sha512-xNHLShzimYbMXqEJoiNyB4fNOXQUwSJCzt9FiOA4GdxucJnCgIM25mXtSj2I93cz+KD39QgRjEYep+li/lykOw==", - "license": "GPL-3.0-or-later", - "peerDependencies": { - "@multiversx/sdk-core": ">= 12.1.0" - } - }, - "node_modules/@multiversx/sdk-hw-provider": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-hw-provider/-/sdk-hw-provider-6.4.0.tgz", - "integrity": "sha512-o+iO64U7gi3oqQHIaCNWoOCarrOQyjXEq7kEsWg/HXJOANpc+lq4GiXPtBkN5mOwPoj/UlEk1HfxA+54mTCFNQ==", - "license": "MIT", - "dependencies": { - "@ledgerhq/devices": "8.0.3", - "@ledgerhq/errors": "6.12.6", - "@ledgerhq/hw-transport": "6.28.4", - "@ledgerhq/hw-transport-web-ble": "6.27.17", - "@ledgerhq/hw-transport-webhid": "6.27.15", - "@ledgerhq/hw-transport-webusb": "6.27.15", - "buffer": "6.0.3", - "platform": "1.3.6" - }, - "peerDependencies": { - "@multiversx/sdk-core": ">= 12.5.0" - } - }, - "node_modules/@multiversx/sdk-metamask-provider": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-metamask-provider/-/sdk-metamask-provider-0.0.7.tgz", - "integrity": "sha512-eqA1z/QIflauv5lqetKw2J5E7UooSTcHbZsxwkquWFnO6j1hj35/odS4P8AcbCOVssenZ+THkLOR7kxx5l7e5g==", - "license": "GPL-3.0-or-later", - "dependencies": { - "@metamask/providers": "16.0.0" - }, - "peerDependencies": { - "@multiversx/sdk-core": ">= 13.0.0" - } - }, - "node_modules/@multiversx/sdk-native-auth-client": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-native-auth-client/-/sdk-native-auth-client-1.0.9.tgz", - "integrity": "sha512-q1/cDRKz7QQsr8lQskUsfGkqJbIut772/MBX52Td4OTGg/G1HAm2xsELe+06y7L537A2rqz5/W9KkJ5yWt968g==", - "license": "GPL-3.0-or-later", - "dependencies": { - "axios": "^1.7.4" - } - }, - "node_modules/@multiversx/sdk-opera-provider": { - "version": "1.0.0-alpha.1", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-opera-provider/-/sdk-opera-provider-1.0.0-alpha.1.tgz", - "integrity": "sha512-5hrqn+kNpuy/S6eV5wh5mE4lvQo0PduZ7fLsh/2Srcaz3K5kM5lE1VyQmWk4DTxToZSldrGbgWz/olezoC6fPg==", - "license": "GPL-3.0-or-later" - }, - "node_modules/@multiversx/sdk-passkey-provider": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-passkey-provider/-/sdk-passkey-provider-1.0.9.tgz", - "integrity": "sha512-9CNQICB7xPUJYzLKYBLzxTDhiwkcmfCxijnP1otjuQDY4AIKyHctVTXfF0C9WreMs0HlNMzJve81+FonhoPjYA==", - "license": "GPL-3.0-or-later", - "dependencies": { - "@noble/ed25519": "2.1.0", - "@types/jest": "29.5.11", - "short-unique-id": "5.2.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@multiversx/sdk-core": ">= 12.18.0", - "@multiversx/sdk-dapp-utils": ">= 0.1.0", - "@multiversx/sdk-wallet": ">=4.5.1" - } - }, - "node_modules/@multiversx/sdk-passkey-provider/node_modules/@noble/ed25519": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-2.1.0.tgz", - "integrity": "sha512-KM4qTyXPinyCgMzeYJH/UudpdL+paJXtY3CHtHYZQtBkS8MZoPr4rOikZllIutJe0d06QDQKisyn02gxZ8TcQA==", - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@multiversx/sdk-transaction-decoder": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-transaction-decoder/-/sdk-transaction-decoder-1.0.2.tgz", - "integrity": "sha512-j43QsKquu8N51WLmVlJ7dV2P3A1448R7/ktvl8r3i6wRMpfdtzDPNofTdHmMRT7DdQdvs4+RNgz8hVKL11Etsw==", - "license": "MIT", - "dependencies": { - "bech32": "^2.0.0" - } - }, - "node_modules/@multiversx/sdk-transaction-decoder/node_modules/bech32": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", - "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==", - "license": "MIT" - }, - "node_modules/@multiversx/sdk-wallet": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-wallet/-/sdk-wallet-4.6.0.tgz", - "integrity": "sha512-SxO/hBTnAB+uWH4MJdwKLvfRk8cxdA8zi6JC4I9j1+V+XQJpfs2YHCvMmaVyIAqjmSg9i7Uk61exrDkR3bjPAw==", - "license": "MIT", - "dependencies": { - "@multiversx/sdk-bls-wasm": "0.3.5", - "@noble/ed25519": "1.7.3", - "@noble/hashes": "1.3.0", - "bech32": "1.1.4", - "bip39": "3.1.0", - "blake2b": "2.1.3", - "ed25519-hd-key": "1.1.2", - "ed2curve": "0.3.0", - "keccak": "3.0.1", - "scryptsy": "2.1.0", - "tweetnacl": "1.0.3", - "uuid": "8.3.2" - } - }, - "node_modules/@multiversx/sdk-wallet-connect-provider": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-wallet-connect-provider/-/sdk-wallet-connect-provider-4.1.4.tgz", - "integrity": "sha512-nAA+LSK+28iBrTbmZOkHaMavQiGr6sdAK9Vhjf5aPpsWYAXOZijqAQnuYg9aDcOT2yCF4L4Ngzzj1qKlW1TpFA==", - "license": "MIT", - "dependencies": { - "@walletconnect/sign-client": "2.17.0", - "@walletconnect/utils": "2.17.0", - "bech32": "1.1.4" - }, - "peerDependencies": { - "@multiversx/sdk-core": ">= 12.8.0" - } - }, - "node_modules/@multiversx/sdk-wallet/node_modules/keccak": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz", - "integrity": "sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@multiversx/sdk-web-wallet-cross-window-provider": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-web-wallet-cross-window-provider/-/sdk-web-wallet-cross-window-provider-1.0.0.tgz", - "integrity": "sha512-xqdKCFpBCxNcp4aSwC2FLbks2Ii2uy5YpHnqR8qnqCnjH6TqdGZ1xKzQauZsiYqseVueVTmynK28w9pTOZ0Oqg==", - "license": "GPL-3.0-or-later", - "dependencies": { - "@types/jest": "^29.5.11", - "@types/qs": "6.9.10", - "qs": "6.11.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@multiversx/sdk-core": ">= 12.18.0", - "@multiversx/sdk-dapp-utils": ">= 0.1.0" - } - }, - "node_modules/@multiversx/sdk-web-wallet-cross-window-provider/node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@multiversx/sdk-web-wallet-iframe-provider": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-web-wallet-iframe-provider/-/sdk-web-wallet-iframe-provider-1.0.3.tgz", - "integrity": "sha512-viKc7nmy66W+kjX6JIPm/adiwieX5K82Cuy9JtFS1xTx/Q7uZJOxktqC2+MIUPKudiYKWY1N07WaM5mzTIu5gQ==", - "license": "GPL-3.0-or-later", - "dependencies": { - "@types/jest": "^29.5.11", - "@types/qs": "6.9.10", - "qs": "6.11.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@multiversx/sdk-core": ">= 12.18.0", - "@multiversx/sdk-dapp-utils": ">= 0.1.0" - } - }, - "node_modules/@multiversx/sdk-web-wallet-iframe-provider/node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@multiversx/sdk-web-wallet-provider": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-web-wallet-provider/-/sdk-web-wallet-provider-3.2.1.tgz", - "integrity": "sha512-f0CHVsBephFhoQtCAx9y2WhiIZEaNgn0oVa+sZNkgILcXU53Gm8Rj8wMfM0SDimHBYcXCcBDedyLCU3MioOjoQ==", - "license": "GPL-3.0-or-later", - "dependencies": { - "qs": "6.10.3" - }, - "peerDependencies": { - "@multiversx/sdk-core": ">= 12.18.0" - } - }, - "node_modules/@multiversx/sdk-webview-provider": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-webview-provider/-/sdk-webview-provider-1.0.0.tgz", - "integrity": "sha512-nxC1BaP319ApbJb51QNi7W9BwUrS7V311H7lVwIUgOgVFrCwdttc/rlvVDrmieQIupqSSCWWKf1/cvbzt621aw==", - "license": "GPL-3.0-or-later", - "peerDependencies": { - "@multiversx/sdk-core": ">= 12.1.0", - "@multiversx/sdk-dapp-utils": ">= 0.1.0" - } - }, - "node_modules/@noble/curves": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", - "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.4.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/curves/node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/ed25519": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.3.tgz", - "integrity": "sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT" - }, - "node_modules/@noble/hashes": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", - "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT" - }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause" - }, - "node_modules/@reduxjs/toolkit": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.8.2.tgz", - "integrity": "sha512-CtPw5TkN1pHRigMFCOS/0qg3b/yfPV5qGCsltVnIz7bx4PKTJlGHYfIxm97qskLknMzuGfjExaYdXJ77QTL0vg==", - "license": "MIT", - "dependencies": { - "immer": "^9.0.7", - "redux": "^4.1.2", - "redux-thunk": "^2.4.1", - "reselect": "^4.1.5" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18", - "react-redux": "^7.2.1 || ^8.0.0-beta" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-redux": { - "optional": true - } - } - }, - "node_modules/@reduxjs/toolkit/node_modules/reselect": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", - "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", - "license": "MIT" - }, - "node_modules/@scure/base": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz", - "integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==", - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", - "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.4.0", - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32/node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32/node_modules/@scure/base": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", - "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip39": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", - "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip39/node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip39/node_modules/@scure/base": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", - "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "license": "MIT" - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "license": "MIT" - }, - "node_modules/@stablelib/aead": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/aead/-/aead-1.0.1.tgz", - "integrity": "sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg==", - "license": "MIT" - }, - "node_modules/@stablelib/binary": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", - "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", - "license": "MIT", - "dependencies": { - "@stablelib/int": "^1.0.1" - } - }, - "node_modules/@stablelib/bytes": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/bytes/-/bytes-1.0.1.tgz", - "integrity": "sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==", - "license": "MIT" - }, - "node_modules/@stablelib/chacha": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/chacha/-/chacha-1.0.1.tgz", - "integrity": "sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg==", - "license": "MIT", - "dependencies": { - "@stablelib/binary": "^1.0.1", - "@stablelib/wipe": "^1.0.1" - } - }, - "node_modules/@stablelib/chacha20poly1305": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/chacha20poly1305/-/chacha20poly1305-1.0.1.tgz", - "integrity": "sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA==", - "license": "MIT", - "dependencies": { - "@stablelib/aead": "^1.0.1", - "@stablelib/binary": "^1.0.1", - "@stablelib/chacha": "^1.0.1", - "@stablelib/constant-time": "^1.0.1", - "@stablelib/poly1305": "^1.0.1", - "@stablelib/wipe": "^1.0.1" - } - }, - "node_modules/@stablelib/constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/constant-time/-/constant-time-1.0.1.tgz", - "integrity": "sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==", - "license": "MIT" - }, - "node_modules/@stablelib/ed25519": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@stablelib/ed25519/-/ed25519-1.0.3.tgz", - "integrity": "sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==", - "license": "MIT", - "dependencies": { - "@stablelib/random": "^1.0.2", - "@stablelib/sha512": "^1.0.1", - "@stablelib/wipe": "^1.0.1" - } - }, - "node_modules/@stablelib/hash": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/hash/-/hash-1.0.1.tgz", - "integrity": "sha512-eTPJc/stDkdtOcrNMZ6mcMK1e6yBbqRBaNW55XA1jU8w/7QdnCF0CmMmOD1m7VSkBR44PWrMHU2l6r8YEQHMgg==", - "license": "MIT" - }, - "node_modules/@stablelib/hkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/hkdf/-/hkdf-1.0.1.tgz", - "integrity": "sha512-SBEHYE16ZXlHuaW5RcGk533YlBj4grMeg5TooN80W3NpcHRtLZLLXvKyX0qcRFxf+BGDobJLnwkvgEwHIDBR6g==", - "license": "MIT", - "dependencies": { - "@stablelib/hash": "^1.0.1", - "@stablelib/hmac": "^1.0.1", - "@stablelib/wipe": "^1.0.1" - } - }, - "node_modules/@stablelib/hmac": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/hmac/-/hmac-1.0.1.tgz", - "integrity": "sha512-V2APD9NSnhVpV/QMYgCVMIYKiYG6LSqw1S65wxVoirhU/51ACio6D4yDVSwMzuTJXWZoVHbDdINioBwKy5kVmA==", - "license": "MIT", - "dependencies": { - "@stablelib/constant-time": "^1.0.1", - "@stablelib/hash": "^1.0.1", - "@stablelib/wipe": "^1.0.1" - } - }, - "node_modules/@stablelib/int": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", - "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==", - "license": "MIT" - }, - "node_modules/@stablelib/keyagreement": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/keyagreement/-/keyagreement-1.0.1.tgz", - "integrity": "sha512-VKL6xBwgJnI6l1jKrBAfn265cspaWBPAPEc62VBQrWHLqVgNRE09gQ/AnOEyKUWrrqfD+xSQ3u42gJjLDdMDQg==", - "license": "MIT", - "dependencies": { - "@stablelib/bytes": "^1.0.1" - } - }, - "node_modules/@stablelib/poly1305": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/poly1305/-/poly1305-1.0.1.tgz", - "integrity": "sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA==", - "license": "MIT", - "dependencies": { - "@stablelib/constant-time": "^1.0.1", - "@stablelib/wipe": "^1.0.1" - } - }, - "node_modules/@stablelib/random": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", - "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", - "license": "MIT", - "dependencies": { - "@stablelib/binary": "^1.0.1", - "@stablelib/wipe": "^1.0.1" - } - }, - "node_modules/@stablelib/sha256": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/sha256/-/sha256-1.0.1.tgz", - "integrity": "sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ==", - "license": "MIT", - "dependencies": { - "@stablelib/binary": "^1.0.1", - "@stablelib/hash": "^1.0.1", - "@stablelib/wipe": "^1.0.1" - } - }, - "node_modules/@stablelib/sha512": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/sha512/-/sha512-1.0.1.tgz", - "integrity": "sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==", - "license": "MIT", - "dependencies": { - "@stablelib/binary": "^1.0.1", - "@stablelib/hash": "^1.0.1", - "@stablelib/wipe": "^1.0.1" - } - }, - "node_modules/@stablelib/wipe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", - "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==", - "license": "MIT" - }, - "node_modules/@stablelib/x25519": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@stablelib/x25519/-/x25519-1.0.3.tgz", - "integrity": "sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==", - "license": "MIT", - "dependencies": { - "@stablelib/keyagreement": "^1.0.1", - "@stablelib/random": "^1.0.2", - "@stablelib/wipe": "^1.0.1" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", - "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", - "license": "MIT", - "dependencies": { - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.11", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", - "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.13.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", - "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "license": "MIT" - }, - "node_modules/@types/qs": { - "version": "6.9.10", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", - "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==", - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.3.18", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", - "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", - "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", - "license": "MIT", - "peer": true, - "peerDependencies": { - "@types/react": "^18.0.0" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "license": "MIT" - }, - "node_modules/@types/use-sync-external-store": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "license": "MIT" - }, - "node_modules/@walletconnect/core": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@walletconnect/core/-/core-2.17.0.tgz", - "integrity": "sha512-On+uSaCfWdsMIQsECwWHZBmUXfrnqmv6B8SXRRuTJgd8tUpEvBkLQH4X7XkSm3zW6ozEkQTCagZ2ox2YPn3kbw==", - "license": "Apache-2.0", - "dependencies": { - "@walletconnect/heartbeat": "1.2.2", - "@walletconnect/jsonrpc-provider": "1.0.14", - "@walletconnect/jsonrpc-types": "1.0.4", - "@walletconnect/jsonrpc-utils": "1.0.8", - "@walletconnect/jsonrpc-ws-connection": "1.0.14", - "@walletconnect/keyvaluestorage": "1.1.1", - "@walletconnect/logger": "2.1.2", - "@walletconnect/relay-api": "1.0.11", - "@walletconnect/relay-auth": "1.0.4", - "@walletconnect/safe-json": "1.0.2", - "@walletconnect/time": "1.0.2", - "@walletconnect/types": "2.17.0", - "@walletconnect/utils": "2.17.0", - "events": "3.3.0", - "lodash.isequal": "4.5.0", - "uint8arrays": "3.1.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@walletconnect/core/node_modules/@walletconnect/keyvaluestorage": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.1.1.tgz", - "integrity": "sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==", - "license": "MIT", - "dependencies": { - "@walletconnect/safe-json": "^1.0.1", - "idb-keyval": "^6.2.1", - "unstorage": "^1.9.0" - }, - "peerDependencies": { - "@react-native-async-storage/async-storage": "1.x" - }, - "peerDependenciesMeta": { - "@react-native-async-storage/async-storage": { - "optional": true - } - } - }, - "node_modules/@walletconnect/core/node_modules/unstorage": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.15.0.tgz", - "integrity": "sha512-m40eHdGY/gA6xAPqo8eaxqXgBuzQTlAKfmB1iF7oCKXE1HfwHwzDJBywK+qQGn52dta+bPlZluPF7++yR3p/bg==", - "license": "MIT", - "dependencies": { - "anymatch": "^3.1.3", - "chokidar": "^4.0.3", - "destr": "^2.0.3", - "h3": "^1.15.0", - "lru-cache": "^10.4.3", - "node-fetch-native": "^1.6.6", - "ofetch": "^1.4.1", - "ufo": "^1.5.4" - }, - "peerDependencies": { - "@azure/app-configuration": "^1.8.0", - "@azure/cosmos": "^4.2.0", - "@azure/data-tables": "^13.3.0", - "@azure/identity": "^4.6.0", - "@azure/keyvault-secrets": "^4.9.0", - "@azure/storage-blob": "^12.26.0", - "@capacitor/preferences": "^6.0.3", - "@deno/kv": ">=0.9.0", - "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0", - "@planetscale/database": "^1.19.0", - "@upstash/redis": "^1.34.3", - "@vercel/blob": ">=0.27.1", - "@vercel/kv": "^1.0.1", - "aws4fetch": "^1.0.20", - "db0": ">=0.2.1", - "idb-keyval": "^6.2.1", - "ioredis": "^5.4.2", - "uploadthing": "^7.4.4" - }, - "peerDependenciesMeta": { - "@azure/app-configuration": { - "optional": true - }, - "@azure/cosmos": { - "optional": true - }, - "@azure/data-tables": { - "optional": true - }, - "@azure/identity": { - "optional": true - }, - "@azure/keyvault-secrets": { - "optional": true - }, - "@azure/storage-blob": { - "optional": true - }, - "@capacitor/preferences": { - "optional": true - }, - "@deno/kv": { - "optional": true - }, - "@netlify/blobs": { - "optional": true - }, - "@planetscale/database": { - "optional": true - }, - "@upstash/redis": { - "optional": true - }, - "@vercel/blob": { - "optional": true - }, - "@vercel/kv": { - "optional": true - }, - "aws4fetch": { - "optional": true - }, - "db0": { - "optional": true - }, - "idb-keyval": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "uploadthing": { - "optional": true - } - } - }, - "node_modules/@walletconnect/environment": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@walletconnect/environment/-/environment-1.0.1.tgz", - "integrity": "sha512-T426LLZtHj8e8rYnKfzsw1aG6+M0BT1ZxayMdv/p8yM0MU+eJDISqNY3/bccxRr4LrF9csq02Rhqt08Ibl0VRg==", - "license": "MIT", - "dependencies": { - "tslib": "1.14.1" - } - }, - "node_modules/@walletconnect/events": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@walletconnect/events/-/events-1.0.1.tgz", - "integrity": "sha512-NPTqaoi0oPBVNuLv7qPaJazmGHs5JGyO8eEAk5VGKmJzDR7AHzD4k6ilox5kxk1iwiOnFopBOOMLs86Oa76HpQ==", - "license": "MIT", - "dependencies": { - "keyvaluestorage-interface": "^1.0.0", - "tslib": "1.14.1" - } - }, - "node_modules/@walletconnect/heartbeat": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@walletconnect/heartbeat/-/heartbeat-1.2.2.tgz", - "integrity": "sha512-uASiRmC5MwhuRuf05vq4AT48Pq8RMi876zV8rr8cV969uTOzWdB/k+Lj5yI2PBtB1bGQisGen7MM1GcZlQTBXw==", - "license": "MIT", - "dependencies": { - "@walletconnect/events": "^1.0.1", - "@walletconnect/time": "^1.0.2", - "events": "^3.3.0" - } - }, - "node_modules/@walletconnect/jsonrpc-provider": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-provider/-/jsonrpc-provider-1.0.14.tgz", - "integrity": "sha512-rtsNY1XqHvWj0EtITNeuf8PHMvlCLiS3EjQL+WOkxEOA4KPxsohFnBDeyPYiNm4ZvkQdLnece36opYidmtbmow==", - "license": "MIT", - "dependencies": { - "@walletconnect/jsonrpc-utils": "^1.0.8", - "@walletconnect/safe-json": "^1.0.2", - "events": "^3.3.0" - } - }, - "node_modules/@walletconnect/jsonrpc-types": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.4.tgz", - "integrity": "sha512-P6679fG/M+wuWg9TY8mh6xFSdYnFyFjwFelxyISxMDrlbXokorEVXYOxiqEbrU3x1BmBoCAJJ+vtEaEoMlpCBQ==", - "license": "MIT", - "dependencies": { - "events": "^3.3.0", - "keyvaluestorage-interface": "^1.0.0" - } - }, - "node_modules/@walletconnect/jsonrpc-utils": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.8.tgz", - "integrity": "sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw==", - "license": "MIT", - "dependencies": { - "@walletconnect/environment": "^1.0.1", - "@walletconnect/jsonrpc-types": "^1.0.3", - "tslib": "1.14.1" - } - }, - "node_modules/@walletconnect/jsonrpc-ws-connection": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.14.tgz", - "integrity": "sha512-Jsl6fC55AYcbkNVkwNM6Jo+ufsuCQRqViOQ8ZBPH9pRREHH9welbBiszuTLqEJiQcO/6XfFDl6bzCJIkrEi8XA==", - "license": "MIT", - "dependencies": { - "@walletconnect/jsonrpc-utils": "^1.0.6", - "@walletconnect/safe-json": "^1.0.2", - "events": "^3.3.0", - "ws": "^7.5.1" - } - }, - "node_modules/@walletconnect/logger": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@walletconnect/logger/-/logger-2.1.2.tgz", - "integrity": "sha512-aAb28I3S6pYXZHQm5ESB+V6rDqIYfsnHaQyzFbwUUBFY4H0OXx/YtTl8lvhUNhMMfb9UxbwEBS253TlXUYJWSw==", - "license": "MIT", - "dependencies": { - "@walletconnect/safe-json": "^1.0.2", - "pino": "7.11.0" - } - }, - "node_modules/@walletconnect/relay-api": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@walletconnect/relay-api/-/relay-api-1.0.11.tgz", - "integrity": "sha512-tLPErkze/HmC9aCmdZOhtVmYZq1wKfWTJtygQHoWtgg722Jd4homo54Cs4ak2RUFUZIGO2RsOpIcWipaua5D5Q==", - "license": "MIT", - "dependencies": { - "@walletconnect/jsonrpc-types": "^1.0.2" - } - }, - "node_modules/@walletconnect/relay-auth": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@walletconnect/relay-auth/-/relay-auth-1.0.4.tgz", - "integrity": "sha512-kKJcS6+WxYq5kshpPaxGHdwf5y98ZwbfuS4EE/NkQzqrDFm5Cj+dP8LofzWvjrrLkZq7Afy7WrQMXdLy8Sx7HQ==", - "license": "MIT", - "dependencies": { - "@stablelib/ed25519": "^1.0.2", - "@stablelib/random": "^1.0.1", - "@walletconnect/safe-json": "^1.0.1", - "@walletconnect/time": "^1.0.2", - "tslib": "1.14.1", - "uint8arrays": "^3.0.0" - } - }, - "node_modules/@walletconnect/safe-json": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@walletconnect/safe-json/-/safe-json-1.0.2.tgz", - "integrity": "sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA==", - "license": "MIT", - "dependencies": { - "tslib": "1.14.1" - } - }, - "node_modules/@walletconnect/sign-client": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.17.0.tgz", - "integrity": "sha512-sErYwvSSHQolNXni47L3Bm10ptJc1s1YoJvJd34s5E9h9+d3rj7PrhbiW9X82deN+Dm5oA8X9tC4xty1yIBrVg==", - "license": "Apache-2.0", - "dependencies": { - "@walletconnect/core": "2.17.0", - "@walletconnect/events": "1.0.1", - "@walletconnect/heartbeat": "1.2.2", - "@walletconnect/jsonrpc-utils": "1.0.8", - "@walletconnect/logger": "2.1.2", - "@walletconnect/time": "1.0.2", - "@walletconnect/types": "2.17.0", - "@walletconnect/utils": "2.17.0", - "events": "3.3.0" - } - }, - "node_modules/@walletconnect/time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@walletconnect/time/-/time-1.0.2.tgz", - "integrity": "sha512-uzdd9woDcJ1AaBZRhqy5rNC9laqWGErfc4dxA9a87mPdKOgWMD85mcFo9dIYIts/Jwocfwn07EC6EzclKubk/g==", - "license": "MIT", - "dependencies": { - "tslib": "1.14.1" - } - }, - "node_modules/@walletconnect/types": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@walletconnect/types/-/types-2.17.0.tgz", - "integrity": "sha512-i1pn9URpvt9bcjRDkabuAmpA9K7mzyKoLJlbsAujRVX7pfaG7wur7u9Jz0bk1HxvuABL5LHNncTnVKSXKQ5jZA==", - "license": "Apache-2.0", - "dependencies": { - "@walletconnect/events": "1.0.1", - "@walletconnect/heartbeat": "1.2.2", - "@walletconnect/jsonrpc-types": "1.0.4", - "@walletconnect/keyvaluestorage": "1.1.1", - "@walletconnect/logger": "2.1.2", - "events": "3.3.0" - } - }, - "node_modules/@walletconnect/types/node_modules/@walletconnect/keyvaluestorage": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.1.1.tgz", - "integrity": "sha512-V7ZQq2+mSxAq7MrRqDxanTzu2RcElfK1PfNYiaVnJgJ7Q7G7hTVwF8voIBx92qsRyGHZihrwNPHuZd1aKkd0rA==", - "license": "MIT", - "dependencies": { - "@walletconnect/safe-json": "^1.0.1", - "idb-keyval": "^6.2.1", - "unstorage": "^1.9.0" - }, - "peerDependencies": { - "@react-native-async-storage/async-storage": "1.x" - }, - "peerDependenciesMeta": { - "@react-native-async-storage/async-storage": { - "optional": true - } - } - }, - "node_modules/@walletconnect/types/node_modules/unstorage": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.15.0.tgz", - "integrity": "sha512-m40eHdGY/gA6xAPqo8eaxqXgBuzQTlAKfmB1iF7oCKXE1HfwHwzDJBywK+qQGn52dta+bPlZluPF7++yR3p/bg==", - "license": "MIT", - "dependencies": { - "anymatch": "^3.1.3", - "chokidar": "^4.0.3", - "destr": "^2.0.3", - "h3": "^1.15.0", - "lru-cache": "^10.4.3", - "node-fetch-native": "^1.6.6", - "ofetch": "^1.4.1", - "ufo": "^1.5.4" - }, - "peerDependencies": { - "@azure/app-configuration": "^1.8.0", - "@azure/cosmos": "^4.2.0", - "@azure/data-tables": "^13.3.0", - "@azure/identity": "^4.6.0", - "@azure/keyvault-secrets": "^4.9.0", - "@azure/storage-blob": "^12.26.0", - "@capacitor/preferences": "^6.0.3", - "@deno/kv": ">=0.9.0", - "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0", - "@planetscale/database": "^1.19.0", - "@upstash/redis": "^1.34.3", - "@vercel/blob": ">=0.27.1", - "@vercel/kv": "^1.0.1", - "aws4fetch": "^1.0.20", - "db0": ">=0.2.1", - "idb-keyval": "^6.2.1", - "ioredis": "^5.4.2", - "uploadthing": "^7.4.4" - }, - "peerDependenciesMeta": { - "@azure/app-configuration": { - "optional": true - }, - "@azure/cosmos": { - "optional": true - }, - "@azure/data-tables": { - "optional": true - }, - "@azure/identity": { - "optional": true - }, - "@azure/keyvault-secrets": { - "optional": true - }, - "@azure/storage-blob": { - "optional": true - }, - "@capacitor/preferences": { - "optional": true - }, - "@deno/kv": { - "optional": true - }, - "@netlify/blobs": { - "optional": true - }, - "@planetscale/database": { - "optional": true - }, - "@upstash/redis": { - "optional": true - }, - "@vercel/blob": { - "optional": true - }, - "@vercel/kv": { - "optional": true - }, - "aws4fetch": { - "optional": true - }, - "db0": { - "optional": true - }, - "idb-keyval": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "uploadthing": { - "optional": true - } - } - }, - "node_modules/@walletconnect/utils": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.17.0.tgz", - "integrity": "sha512-1aeQvjwsXy4Yh9G6g2eGmXrEl+BzkNjHRdCrGdMYqFTFa8ROEJfTGsSH3pLsNDlOY94CoBUvJvM55q/PMoN/FQ==", - "license": "Apache-2.0", - "dependencies": { - "@stablelib/chacha20poly1305": "1.0.1", - "@stablelib/hkdf": "1.0.1", - "@stablelib/random": "1.0.2", - "@stablelib/sha256": "1.0.1", - "@stablelib/x25519": "1.0.3", - "@walletconnect/relay-api": "1.0.11", - "@walletconnect/relay-auth": "1.0.4", - "@walletconnect/safe-json": "1.0.2", - "@walletconnect/time": "1.0.2", - "@walletconnect/types": "2.17.0", - "@walletconnect/window-getters": "1.0.1", - "@walletconnect/window-metadata": "1.0.1", - "detect-browser": "5.3.0", - "elliptic": "^6.5.7", - "query-string": "7.1.3", - "uint8arrays": "3.1.0" - } - }, - "node_modules/@walletconnect/window-getters": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@walletconnect/window-getters/-/window-getters-1.0.1.tgz", - "integrity": "sha512-vHp+HqzGxORPAN8gY03qnbTMnhqIwjeRJNOMOAzePRg4xVEEE2WvYsI9G2NMjOknA8hnuYbU3/hwLcKbjhc8+Q==", - "license": "MIT", - "dependencies": { - "tslib": "1.14.1" - } - }, - "node_modules/@walletconnect/window-metadata": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@walletconnect/window-metadata/-/window-metadata-1.0.1.tgz", - "integrity": "sha512-9koTqyGrM2cqFRW517BPY/iEtUDx2r1+Pwwu5m7sJ7ka79wi3EyqhqcICk/yDmv6jAS1rjKgTKXlEhanYjijcA==", - "license": "MIT", - "dependencies": { - "@walletconnect/window-getters": "^1.0.1", - "tslib": "1.14.1" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bech32": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", - "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", - "license": "MIT" - }, - "node_modules/bignumber.js": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", - "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/bip39": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz", - "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==", - "license": "ISC", - "dependencies": { - "@noble/hashes": "^1.2.0" - } - }, - "node_modules/blake2b": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/blake2b/-/blake2b-2.1.3.tgz", - "integrity": "sha512-pkDss4xFVbMb4270aCyGD3qLv92314Et+FsKzilCLxDz5DuZ2/1g3w4nmBbu6nKApPspnjG7JcwTjGZnduB1yg==", - "license": "ISC", - "dependencies": { - "blake2b-wasm": "^1.1.0", - "nanoassert": "^1.0.0" - } - }, - "node_modules/blake2b-wasm": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/blake2b-wasm/-/blake2b-wasm-1.1.7.tgz", - "integrity": "sha512-oFIHvXhlz/DUgF0kq5B1CqxIDjIJwh9iDeUUGQUcvgiGz7Wdw03McEO7CfLBy7QKGdsydcMCgO9jFNBAFCtFcA==", - "license": "MIT", - "dependencies": { - "nanoassert": "^1.0.0" - } - }, - "node_modules/bn.js": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", - "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "license": "MIT" - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/chai": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.0.tgz", - "integrity": "sha512-kDZ7MZyM6Q1DhR9jy7dalKohXQ2yrlXkk59CR52aRKxJrobmlBNqnFQxX9xOX8w+4mz8SYlKJa/7D7ddltFXCw==", - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.0.0", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cipher-base": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz", - "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.4", - "safe-buffer": "^5.2.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/classnames": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", - "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==", - "license": "MIT", - "optional": true - }, - "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/cookie-es": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", - "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", - "license": "MIT" - }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "license": "MIT", - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "license": "MIT", - "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "node_modules/crossws": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.4.tgz", - "integrity": "sha512-uj0O1ETYX1Bh6uSgktfPvwDiPYGQ3aI4qVsaC/LWpkIzGj1nUYm5FK3K+t11oOlpN01lGbprFCH4wBlKdJjVgw==", - "license": "MIT", - "dependencies": { - "uncrypto": "^0.1.3" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/defu": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "license": "MIT" - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/destr": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.3.tgz", - "integrity": "sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==", - "license": "MIT" - }, - "node_modules/detect-browser": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/detect-browser/-/detect-browser-5.3.0.tgz", - "integrity": "sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==", - "license": "MIT" - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dijkstrajs": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", - "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", - "license": "MIT", - "optional": true - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/duplexify": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.2" - } - }, - "node_modules/ed25519-hd-key": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/ed25519-hd-key/-/ed25519-hd-key-1.1.2.tgz", - "integrity": "sha512-/0y9y6N7vM6Kj5ASr9J9wcMVDTtygxSOvYX+PJiMD7VcxCx2G03V5bLRl8Dug9EgkLFsLhGqBtQWQRcElEeWTA==", - "license": "MIT", - "dependencies": { - "bip39": "3.0.2", - "create-hmac": "1.1.7", - "tweetnacl": "1.0.3" - } - }, - "node_modules/ed25519-hd-key/node_modules/@types/node": { - "version": "11.11.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", - "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==", - "license": "MIT" - }, - "node_modules/ed25519-hd-key/node_modules/bip39": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.2.tgz", - "integrity": "sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ==", - "license": "ISC", - "dependencies": { - "@types/node": "11.11.6", - "create-hash": "^1.1.0", - "pbkdf2": "^3.0.9", - "randombytes": "^2.0.1" - } - }, - "node_modules/ed2curve": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/ed2curve/-/ed2curve-0.3.0.tgz", - "integrity": "sha512-8w2fmmq3hv9rCrcI7g9hms2pMunQr1JINfcjwR9tAyZqhtyaMN991lF/ZfHfr5tzZQ8c7y7aBgZbjfbd0fjFwQ==", - "license": "Unlicense", - "dependencies": { - "tweetnacl": "1.x.x" - } - }, - "node_modules/elliptic": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", - "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", - "license": "MIT", - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT", - "optional": true - }, - "node_modules/encode-utf8": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", - "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==", - "license": "MIT", - "optional": true - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io-client": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz", - "integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==", - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", - "ws": "~8.11.0", - "xmlhttprequest-ssl": "~2.0.0" - } - }, - "node_modules/engine.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/engine.io-parser": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.7.tgz", - "integrity": "sha512-P+jDFbvK6lE3n1OL+q9KuzdOFWkkZ/cMV9gol/SbVfpyqfvrfrFTOFJ6fQm2VC3PZHlU3QPhVwmbsCnauHF2MQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ethereum-cryptography": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", - "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", - "license": "MIT", - "dependencies": { - "@noble/curves": "1.4.2", - "@noble/hashes": "1.4.0", - "@scure/bip32": "1.4.0", - "@scure/bip39": "1.3.0" - } - }, - "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/extension-port-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/extension-port-stream/-/extension-port-stream-3.0.0.tgz", - "integrity": "sha512-an2S5quJMiy5bnZKEf6AkfH/7r8CzHvhchU40gxN+OM6HPhe7Z9T1FUychcf2M9PpPOO0Hf7BAEfJkw2TDIBDw==", - "license": "ISC", - "dependencies": { - "readable-stream": "^3.6.2 || ^4.4.2", - "webextension-polyfill": ">=0.10.0 <1.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-redact": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", - "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "license": "MIT" - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/filter-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", - "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "license": "MIT", - "optional": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "optional": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/h3": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.1.tgz", - "integrity": "sha512-+ORaOBttdUm1E2Uu/obAyCguiI7MbBvsLTndc3gyK3zU+SYLoZXlyCP9Xgy0gikkGufFLTZXCXD6+4BsufnmHA==", - "license": "MIT", - "dependencies": { - "cookie-es": "^1.2.2", - "crossws": "^0.3.3", - "defu": "^6.1.4", - "destr": "^2.0.3", - "iron-webcrypto": "^1.2.1", - "node-mock-http": "^1.0.0", - "radix3": "^1.1.2", - "ufo": "^1.5.4", - "uncrypto": "^0.1.3" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "license": "MIT", - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/idb-keyval": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", - "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==", - "license": "Apache-2.0" - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/iron-webcrypto": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", - "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/brc-dd" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0" - } - }, - "node_modules/keccak": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", - "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/keyvaluestorage-interface": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/keyvaluestorage-interface/-/keyvaluestorage-interface-1.0.0.tgz", - "integrity": "sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==", - "license": "MIT" - }, - "node_modules/linkify-react": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/linkify-react/-/linkify-react-4.0.2.tgz", - "integrity": "sha512-WFHnwOUo6EeKwrQQy1d+UjeKsv+SPQ9toPpaRIXHV1CMo+0kgZBSIsEBxQrFQIEy7WD20QD+sPwNNaJJpynN6g==", - "license": "MIT", - "peerDependencies": { - "linkifyjs": "^4.0.0", - "react": ">= 15.0.0" - } - }, - "node_modules/linkifyjs": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.0.2.tgz", - "integrity": "sha512-/VSoCZiglX0VMsXmL5PN3lRg45M86lrD9PskdkA2abWaTKap1bIcJ11LS4EE55bcUl9ZOR4eZ792UtQ9E/5xLA==", - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "license": "MIT", - "optional": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "license": "MIT" - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", - "license": "MIT" - }, - "node_modules/lodash.omit": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", - "deprecated": "This package is deprecated. Use destructuring assignment syntax instead.", - "license": "MIT" - }, - "node_modules/lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", - "license": "MIT" - }, - "node_modules/lodash.uniqby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", - "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", - "license": "MIT" - }, - "node_modules/long": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz", - "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==", - "license": "Apache-2.0" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "license": "MIT", - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/micro-ftch": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", - "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "license": "ISC" - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "license": "MIT" - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/multiformats": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", - "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", - "license": "(Apache-2.0 AND MIT)" - }, - "node_modules/nanoassert": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz", - "integrity": "sha512-C40jQ3NzfkP53NsO8kEOFd79p4b9kDXQMwgiY1z8ZwrDZgUyom0AHwGegF4Dm99L+YoYhuaB0ceerUcXmqr1rQ==", - "license": "ISC" - }, - "node_modules/node-addon-api": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", - "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", - "license": "MIT" - }, - "node_modules/node-fetch-native": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz", - "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==", - "license": "MIT" - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-mock-http": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.0.tgz", - "integrity": "sha512-0uGYQ1WQL1M5kKvGRXWQ3uZCHtLTO8hln3oBjIusM75WoesZ909uQJs/Hb946i2SS+Gsrhkaa6iAO17jRIv6DQ==", - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ofetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.4.1.tgz", - "integrity": "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==", - "license": "MIT", - "dependencies": { - "destr": "^2.0.3", - "node-fetch-native": "^1.6.4", - "ufo": "^1.5.4" - } - }, - "node_modules/on-exit-leak-free": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz", - "integrity": "sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==", - "license": "MIT" - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "license": "MIT", - "optional": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "license": "MIT", - "optional": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "license": "MIT", - "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - }, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pino": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-7.11.0.tgz", - "integrity": "sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==", - "license": "MIT", - "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.0.0", - "on-exit-leak-free": "^0.2.0", - "pino-abstract-transport": "v0.5.0", - "pino-std-serializers": "^4.0.0", - "process-warning": "^1.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.1.0", - "safe-stable-stringify": "^2.1.0", - "sonic-boom": "^2.2.1", - "thread-stream": "^0.15.1" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/pino-abstract-transport": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz", - "integrity": "sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==", - "license": "MIT", - "dependencies": { - "duplexify": "^4.1.2", - "split2": "^4.0.0" - } - }, - "node_modules/pino-std-serializers": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz", - "integrity": "sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==", - "license": "MIT" - }, - "node_modules/platform": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", - "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", - "license": "MIT" - }, - "node_modules/pngjs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", - "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/pony-cause": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", - "integrity": "sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==", - "license": "0BSD", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/process-warning": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-1.0.0.tgz", - "integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==", - "license": "MIT" - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "optional": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT", - "optional": true - }, - "node_modules/protobufjs": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", - "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/qrcode": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.0.tgz", - "integrity": "sha512-9MgRpgVc+/+47dFvQeD6U2s0Z92EsKzcHogtum4QB+UNd025WOJSHvn/hjk9xmzj7Stj95CyUAs31mrjxliEsQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "dijkstrajs": "^1.0.1", - "encode-utf8": "^1.0.3", - "pngjs": "^5.0.0", - "yargs": "^15.3.1" - }, - "bin": { - "qrcode": "bin/qrcode" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/qs": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", - "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/query-string": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", - "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", - "license": "MIT", - "dependencies": { - "decode-uri-component": "^0.2.2", - "filter-obj": "^1.1.0", - "split-on-first": "^1.0.0", - "strict-uri-encode": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", - "license": "MIT" - }, - "node_modules/radix3": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", - "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", - "license": "MIT" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/react-idle-timer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/react-idle-timer/-/react-idle-timer-5.0.0.tgz", - "integrity": "sha512-41ZoZnGJQH4P7v22pWi5rx1kBEjcOdHIB9OFEvyKtj4UX3T4hB3rJya566QZsFSYMmOA9+rgaXc1JSXy7c+g9A==", - "license": "MIT", - "peerDependencies": { - "react": ">=16", - "react-dom": ">=16" - } - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, - "node_modules/react-redux": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.2.tgz", - "integrity": "sha512-nBwiscMw3NoP59NFCXFf02f8xdo+vSHT/uZ1ldDwF7XaTpzm+Phk97VT4urYBl5TYAPNVaFm12UHAEyzkpNzRA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.1", - "@types/hoist-non-react-statics": "^3.3.1", - "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", - "react-is": "^18.0.0", - "use-sync-external-store": "^1.0.0" - }, - "peerDependencies": { - "@types/react": "^16.8 || ^17.0 || ^18.0", - "@types/react-dom": "^16.8 || ^17.0 || ^18.0", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0", - "react-native": ">=0.59", - "redux": "^4" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/real-require": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.1.0.tgz", - "integrity": "sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==", - "license": "MIT", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.9.2" - } - }, - "node_modules/redux-persist": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", - "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", - "license": "MIT", - "peerDependencies": { - "redux": ">4.0.0" - } - }, - "node_modules/redux-thunk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", - "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", - "license": "MIT", - "peerDependencies": { - "redux": "^4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "license": "ISC", - "optional": true - }, - "node_modules/reselect": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.0.0.tgz", - "integrity": "sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==", - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "license": "MIT", - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/scryptsy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", - "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "license": "ISC", - "optional": true - }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "license": "(MIT AND BSD-3-Clause)", - "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - }, - "bin": { - "sha.js": "bin.js" - } - }, - "node_modules/shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "license": "BSD-3-Clause", - "dependencies": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - }, - "bin": { - "shjs": "bin/shjs" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/short-unique-id": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-5.2.0.tgz", - "integrity": "sha512-cMGfwNyfDZ/nzJ2k2M+ClthBIh//GlZl1JEf47Uoa9XR11bz8Pa2T2wQO4bVrRdH48LrIDWJahQziKo3MjhsWg==", - "license": "Apache-2.0", - "bin": { - "short-unique-id": "bin/short-unique-id", - "suid": "bin/short-unique-id" - } - }, - "node_modules/shx": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", - "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.3", - "shelljs": "^0.8.5" - }, - "bin": { - "shx": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/socket.io-client": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.6.1.tgz", - "integrity": "sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ==", - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.2", - "engine.io-client": "~6.4.0", - "socket.io-parser": "~4.2.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-client/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io-parser/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/sonic-boom": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-2.8.0.tgz", - "integrity": "sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==", - "license": "MIT", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/split-on-first": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", - "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", - "license": "MIT" - }, - "node_modules/strict-uri-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", - "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "optional": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/swr": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.0.tgz", - "integrity": "sha512-AjqHOv2lAhkuUdIiBu9xbuettzAzWXmCEcLONNKJRba87WAefz8Ca9d6ds/SzrPc235n1IxWYdhJ2zF3MNUaoQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "use-sync-external-store": "^1.2.0" - }, - "peerDependencies": { - "react": "^16.11.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/thread-stream": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-0.15.2.tgz", - "integrity": "sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==", - "license": "MIT", - "dependencies": { - "real-require": "^0.1.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "license": "0BSD" - }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "license": "Unlicense" - }, - "node_modules/ufo": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", - "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", - "license": "MIT" - }, - "node_modules/uint8arrays": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.1.0.tgz", - "integrity": "sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog==", - "license": "MIT", - "dependencies": { - "multiformats": "^9.4.2" - } - }, - "node_modules/uncrypto": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", - "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "license": "MIT" - }, - "node_modules/use-sync-external-store": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", - "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/webextension-polyfill": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/webextension-polyfill/-/webextension-polyfill-0.10.0.tgz", - "integrity": "sha512-c5s35LgVa5tFaHhrZDnr3FpQpjj1BB+RXhLTYUxGqBVN460HkbM8TBtEqdXWbpTKfzwCcjAZVF7zXCYSKtcp9g==", - "license": "MPL-2.0" - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "license": "ISC", - "optional": true - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "license": "MIT", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xmlhttprequest-ssl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", - "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "license": "ISC", - "optional": true - }, - "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "license": "MIT", - "optional": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - } - } -} diff --git a/cookbook/package.json b/cookbook/package.json deleted file mode 100644 index 7d879542..00000000 --- a/cookbook/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "type": "module", - "dependencies": { - "@multiversx/sdk-core": "14.0.0", - "@multiversx/sdk-dapp": "2.40.11", - "axios": "1.7.7", - "chai": "5.1.0" - } -} From 570c00dfb24480409e37a86fd33ae88ebb9727af Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 16 Apr 2025 16:25:09 +0300 Subject: [PATCH 03/15] ignore cookbook --- .eslintrc.js | 10 +++++++++- tsconfig.json | 3 ++- tsconfig.tests.json | 3 ++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index c2294fc7..3788ff70 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,7 +10,15 @@ module.exports = { env: { node: true, }, - ignorePatterns: [".eslintrc.js", "node_modules", "out", "out-tests", "out-browser", "out-browser-tests"], + ignorePatterns: [ + ".eslintrc.js", + "node_modules", + "out", + "out-tests", + "out-browser", + "out-browser-tests", + "cookbook", + ], rules: { "@typescript-eslint/interface-name-prefix": "off", "@typescript-eslint/explicit-function-return-type": "off", diff --git a/tsconfig.json b/tsconfig.json index 089ef306..013411e6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -29,6 +29,7 @@ "out-browser", "out-browser-tests", "**/*.spec.ts", - "src/testutils" + "src/testutils", + "cookbook" ] } diff --git a/tsconfig.tests.json b/tsconfig.tests.json index 52a987db..df91468d 100644 --- a/tsconfig.tests.json +++ b/tsconfig.tests.json @@ -27,6 +27,7 @@ "out", "out-tests", "out-browser", - "out-browser-tests" + "out-browser-tests", + "cookbook" ] } From 49bd95f966384c33cccbfe45da7809e2c3a89cac Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 16 Apr 2025 16:26:17 +0300 Subject: [PATCH 04/15] Ignore cookbook --- .npmignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.npmignore b/.npmignore index f40235d5..77edfee9 100644 --- a/.npmignore +++ b/.npmignore @@ -1,3 +1,4 @@ src/testdata/** src/testutils/** localnet.toml +cookbook From 3dbea0bcb8bc0deebecee3d19eb8f7e3d5dae8e6 Mon Sep 17 00:00:00 2001 From: danielailie Date: Thu, 17 Apr 2025 14:11:32 +0300 Subject: [PATCH 05/15] Update documents files --- cookbook/account.js | 137 - cookbook/account.ts | 134 + cookbook/accountManagement.js | 258 -- cookbook/accountManagement.ts | 238 ++ cookbook/addresses.js | 85 - cookbook/addresses.ts | 74 + cookbook/cookbook.md | 5182 +++++++++++++++++---------------- cookbook/delegation.js | 383 --- cookbook/delegation.ts | 370 +++ cookbook/entrypoints.js | 34 - cookbook/entrypoints.ts | 34 + cookbook/generate.py | 38 +- cookbook/guarded.js | 130 - cookbook/guarded.ts | 127 + cookbook/networkProviders.js | 447 --- cookbook/networkProviders.ts | 461 +++ cookbook/relayed.js | 149 - cookbook/relayed.ts | 146 + cookbook/signingObjects.js | 118 - cookbook/signingObjects.ts | 121 + cookbook/smartContracts.js | 533 ---- cookbook/smartContracts.ts | 570 ++++ cookbook/tokens.js | 417 --- cookbook/tokens.ts | 396 +++ cookbook/transactions.js | 205 -- cookbook/transactions.ts | 225 ++ cookbook/verifySIgnatures.js | 147 - cookbook/verifySignatures.ts | 165 ++ cookbook/wallet.pem | 5 + cookbook/wallets.js | 141 - cookbook/wallets.ts | 145 + src/wallet/userWallet.ts | 3 + 32 files changed, 5830 insertions(+), 5788 deletions(-) delete mode 100644 cookbook/account.js create mode 100644 cookbook/account.ts delete mode 100644 cookbook/accountManagement.js create mode 100644 cookbook/accountManagement.ts delete mode 100644 cookbook/addresses.js create mode 100644 cookbook/addresses.ts delete mode 100644 cookbook/delegation.js create mode 100644 cookbook/delegation.ts delete mode 100644 cookbook/entrypoints.js create mode 100644 cookbook/entrypoints.ts delete mode 100644 cookbook/guarded.js create mode 100644 cookbook/guarded.ts delete mode 100644 cookbook/networkProviders.js create mode 100644 cookbook/networkProviders.ts delete mode 100644 cookbook/relayed.js create mode 100644 cookbook/relayed.ts delete mode 100644 cookbook/signingObjects.js create mode 100644 cookbook/signingObjects.ts delete mode 100644 cookbook/smartContracts.js create mode 100644 cookbook/smartContracts.ts delete mode 100644 cookbook/tokens.js create mode 100644 cookbook/tokens.ts delete mode 100644 cookbook/transactions.js create mode 100644 cookbook/transactions.ts delete mode 100644 cookbook/verifySIgnatures.js create mode 100644 cookbook/verifySignatures.ts create mode 100644 cookbook/wallet.pem delete mode 100644 cookbook/wallets.js create mode 100644 cookbook/wallets.ts diff --git a/cookbook/account.js b/cookbook/account.js deleted file mode 100644 index 925c01f6..00000000 --- a/cookbook/account.js +++ /dev/null @@ -1,137 +0,0 @@ -import { Account, DevnetEntrypoint, KeyPair, Mnemonic, UserSecretKey } from '@multiversx/sdk-core'; // md-ignore -import path from 'path'; // md-ignore - -// ## Creating Accounts - -// You can initialize an account directly from the entrypoint. Keep in mind that the account is network agnostic, meaning it doesn't matter which entrypoint is used. -// Accounts are used for signing transactions and messages and managing the account's nonce. They can also be saved to a PEM or keystore file for future use. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const account = entrypoint.createAccount(); -} -// ``` - -// ### Other Ways to Instantiate an Account - -// #### From a Secret Key -// ```js -{ - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); - - const accountFromSecretKey = new Account(secretKey); -} -// ``` - -// #### From a PEM file -// ```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const accountFromPem = Account.newFromPem(filePath); -} -// ``` - -// #### From a Keystore File -// ```js -{ - const keystorePath = path.join("src", "testdata", "testwallets", "alice.json"); - const accountFromKeystore = Account.newFromKeystore({ - filePath: keystorePath, - password: "password" - }); -} -// ``` - -// #### From a Mnemonic -// ```js - -const mnemonic = Mnemonic.generate(); -const accountFromMnemonic = Account.newFromMnemonic(mnemonic.getText()); -// ``` - -// #### From a KeyPair - -// ```js -const keypair = KeyPair.generate(); -const accountFromKeyPairs = Account.newFromKeypair(keypair); -// ``` - -// ### Managing the Account Nonce - -// An account has a `nonce` property that the user is responsible for managing. -// You can fetch the nonce from the network and increment it after each transaction. -// Each transaction must have the correct nonce, otherwise it will fail to execute. - -// ```js -{ - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const key = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); - - const accountWithNonce = new Account(key); - const entrypoint = new DevnetEntrypoint(); - - // Fetch the current nonce from the network // md-as-comment - accountWithNonce.nonce = await entrypoint.recallAccountNonce(accountWithNonce.address); - - // Create and send a transaction here... - - // Increment nonce after each transaction // md-as-comment - const nonce = accountWithNonce.getNonceThenIncrement(); -} -// ``` - -// For more details, see the [Creating Transactions](#creating-transactions) section. - -// #### Saving the Account to a File - -// Accounts can be saved to either a PEM file or a keystore file. -// While PEM wallets are less secure for storing secret keys, they are convenient for testing purposes. -// Keystore files offer a higher level of security. - -// #### Saving the Account to a PEM File -// ```js -{ - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); - - const account = new Account(secretKey); - account.saveToPem({ path: path.resolve("wallet.pem") }); -} -// ``` - -// #### Saving the Account to a Keystore File -// ```js -{ - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); - - const account = new Account(secretKey); - account.saveToKeystore({ - path: path.resolve("keystoreWallet.json"), - password: "password" - }); -} - -// ``` - -// ### Using a Ledger Device - -// You can manage your account with a Ledger device, allowing you to sign both transactions and messages while keeping your keys secure. - -// Note: **The multiversx-sdk package does not include Ledger support by default. To enable it, install the package with Ledger dependencies**: -/* // md-ignore -// ```bash -npm install @multiversx/sdk-hw-provider -// ``` -*/ // md-ignore - -// #### Creating a Ledger Account -// This can be done using the dedicated library. You can find more information [here](https://docs.multiversx.com/sdk-and-tools/sdk-js/sdk-js-signing-providers/#the-hardware-wallet-provider). - -// When signing transactions or messages, the Ledger device will prompt you to confirm the details before proceeding. - -// ### Compatibility with IAccount Interface - -// The `Account` implements the `IAccount` interface, making it compatible with transaction controllers and any other component that expects this interface. diff --git a/cookbook/account.ts b/cookbook/account.ts new file mode 100644 index 00000000..bdad9bba --- /dev/null +++ b/cookbook/account.ts @@ -0,0 +1,134 @@ +import path from "path"; // md-ignore +import { Account, DevnetEntrypoint, KeyPair, Mnemonic, UserSecretKey } from "../src"; // md-ignore +(async () => { + // ## Creating Accounts + + // You can initialize an account directly from the entrypoint. Keep in mind that the account is network agnostic, meaning it doesn't matter which entrypoint is used. + // Accounts are used for signing transactions and messages and managing the account's nonce. They can also be saved to a PEM or keystore file for future use. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const account = entrypoint.createAccount(); + } + // ``` + + // ### Other Ways to Instantiate an Account + + // #### From a Secret Key + // ```js + { + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); + + const accountFromSecretKey = new Account(secretKey); + } + // ``` + + // #### From a PEM file + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const accountFromPem = Account.newFromPem(filePath); + } + // ``` + + // #### From a Keystore File + // ```js + { + const keystorePath = path.join("../src", "testdata", "testwallets", "alice.json"); + const accountFromKeystore = Account.newFromKeystore(keystorePath, "password"); + } + // ``` + + // #### From a Mnemonic + // ```js + + const mnemonic = Mnemonic.generate(); + const accountFromMnemonic = Account.newFromMnemonic(mnemonic.toString()); + // ``` + + // #### From a KeyPair + + // ```js + const keypair = KeyPair.generate(); + const accountFromKeyPairs = Account.newFromKeypair(keypair); + // ``` + + // ### Managing the Account Nonce + + // An account has a `nonce` property that the user is responsible for managing. + // You can fetch the nonce from the network and increment it after each transaction. + // Each transaction must have the correct nonce, otherwise it will fail to execute. + + // ```js + { + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const key = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); + + const accountWithNonce = new Account(key); + const entrypoint = new DevnetEntrypoint(); + + // Fetch the current nonce from the network // md-as-comment + accountWithNonce.nonce = await entrypoint.recallAccountNonce(accountWithNonce.address); + + // Create and send a transaction here... + + // Increment nonce after each transaction // md-as-comment + const nonce = accountWithNonce.getNonceThenIncrement(); + } + // ``` + + // For more details, see the [Creating Transactions](#creating-transactions) section. + + // #### Saving the Account to a File + + // Accounts can be saved to either a PEM file or a keystore file. + // While PEM wallets are less secure for storing secret keys, they are convenient for testing purposes. + // Keystore files offer a higher level of security. + + // #### Saving the Account to a PEM File + // ```js + { + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); + + const account = new Account(secretKey); + account.saveToPem(path.resolve("wallet.pem")); + } + // ``` + + // #### Saving the Account to a Keystore File + // ```js + { + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); + + const account = new Account(secretKey); + account.saveToKeystore(path.resolve("keystoreWallet.json"), "password"); + } + + // ``` + + // ### Using a Ledger Device + + // You can manage your account with a Ledger device, allowing you to sign both transactions and messages while keeping your keys secure. + + // Note: **The multiversx-sdk package does not include Ledger support by default. To enable it, install the package with Ledger dependencies**: + /* // md-ignore +// ```bash +npm install @multiversx/sdk-hw-provider +// ``` +*/ // md-ignore + + // #### Creating a Ledger Account + // This can be done using the dedicated library. You can find more information [here](https://docs.multiversx.com/sdk-and-tools/sdk-js/sdk-js-signing-providers/#the-hardware-wallet-provider). + + // When signing transactions or messages, the Ledger device will prompt you to confirm the details before proceeding. + + // ### Compatibility with IAccount Interface + + // The `Account` implements the `IAccount` interface, making it compatible with transaction controllers and any other component that expects this interface. +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/accountManagement.js b/cookbook/accountManagement.js deleted file mode 100644 index b870f26e..00000000 --- a/cookbook/accountManagement.js +++ /dev/null @@ -1,258 +0,0 @@ - -import { Account, Address, DevnetEntrypoint } from "@multiversx/sdk-core"; // md-ignore -import path from 'path'; // md-ignore - -// ### Account management - -// The account management controller and factory allow us to create transactions for managing accounts, such as: -// - Guarding and unguarding accounts -// - Saving key-value pairs in the account storage, on the blockchain. - -// To learn more about Guardians, please refer to the [official documentation](https://docs.multiversx.com/developers/built-in-functions/#setguardian). -// A guardian can also be set using the WebWallet, which leverages our hosted `Trusted Co-Signer Service`. Follow [this guide](https://docs.multiversx.com/wallet/web-wallet/#guardian) for step-by-step instructions on guarding an account using the wallet. - -// #### Guarding an account using the controller -// ```js -{ - // create the entrypoint and the account controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createAccountController(); - - // create the account to guard // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // we can use a trusted service that provides a guardian, or simply set another address we own or trust // md-as-comment - const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - const transaction = await controller.createTransactionForSettingGuardian( - alice, - alice.getNonceThenIncrement(), - { - guardianAddress: guardian, - serviceID: "SelfOwnedAddress", // this is just an example // md-as-comment - }, - ); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Guarding an account using the factory -// ```js -{ - // create the entrypoint and the account management factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createAccountTransactionsFactory(); - - // create the account to guard // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // we can use a trusted service that provides a guardian, or simply set another address we own or trust // md-as-comment - const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - const transaction = await factory.createTransactionForSettingGuardian( - alice, - { - guardianAddress: guardian, - serviceID: "SelfOwnedAddress", // this is just an example // md-as-comment - }, - ); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // set the nonce // md-as-comment - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// Once a guardian is set, we must wait **20 epochs** before it can be activated. After activation, all transactions sent from the account must also be signed by the guardian. - -// #### Activating the guardian using the controller -// ```js -{ - // create the entrypoint and the account controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createAccountController(); - - // create the account to guard // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = await controller.createTransactionForGuardingAccount( - alice, - alice.getNonceThenIncrement(), - ); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Activating the guardian using the factory -// ```js -{ - // create the entrypoint and the account factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createAccountTransactionsFactory(); - - // create the account to guard // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const transaction = await factory.createTransactionForGuardingAccount( - alice.address, - ); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // set the nonce // md-as-comment - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Unguarding the account using the controller -// ```js -{ - // create the entrypoint and the account controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createAccountController(); - - // create the account to unguard // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - const transaction = await controller.createTransactionForUnguardingAccount( - alice, - alice.getNonceThenIncrement(), - { - guardian: guardian - } - ); - - // the transaction should also be signed by the guardian before being sent otherwise it won't be executed // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Unguarding the guardian using the factory -// ```js -{ - // create the entrypoint and the account factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createAccountTransactionsFactory(); - - // create the account to guard // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - const transaction = await factory.createTransactionForUnguardingAccount( - alice.address, - guardian - ); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // set the nonce // md-as-comment - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Saving a key-value pair to an account using the controller -// You can find more information [here](https://docs.multiversx.com/developers/account-storage) regarding the account storage. - -// ```js -{ - // create the entrypoint and the account controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createAccountController(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // creating the key-value pairs we want to save // md-as-comment - const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - - const transaction = await controller.createTransactionForSavingKeyValue( - alice, - alice.getNonceThenIncrement(), - { - keyValuePairs: keyValuePairs - } - ); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Saving a key-value pair to an account using the factory -// ```js -{ - // create the entrypoint and the account factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createAccountTransactionsFactory(); - - // create the account to guard // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // creating the key-value pairs we want to save // md-as-comment - const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); - - const transaction = await factory.createTransactionForSavingKeyValue( - alice.address, { - keyValuePairs: keyValuePairs, - }); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // set the nonce // md-as-comment - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` diff --git a/cookbook/accountManagement.ts b/cookbook/accountManagement.ts new file mode 100644 index 00000000..b7a8b3f9 --- /dev/null +++ b/cookbook/accountManagement.ts @@ -0,0 +1,238 @@ +import path from "path"; // md-ignore +import { Account, Address, DevnetEntrypoint } from "../src"; // md-ignore +(async () => { + // ### Account management + + // The account management controller and factory allow us to create transactions for managing accounts, such as: + // - Guarding and unguarding accounts + // - Saving key-value pairs in the account storage, on the blockchain. + + // To learn more about Guardians, please refer to the [official documentation](https://docs.multiversx.com/developers/built-in-functions/#setguardian). + // A guardian can also be set using the WebWallet, which leverages our hosted `Trusted Co-Signer Service`. Follow [this guide](https://docs.multiversx.com/wallet/web-wallet/#guardian) for step-by-step instructions on guarding an account using the wallet. + + // #### Guarding an account using the controller + // ```js + { + // create the entrypoint and the account controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); + + // create the account to guard // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // we can use a trusted service that provides a guardian, or simply set another address we own or trust // md-as-comment + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await controller.createTransactionForSettingGuardian(alice, alice.getNonceThenIncrement(), { + guardianAddress: guardian, + serviceID: "SelfOwnedAddress", // this is just an example // md-as-comment + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Guarding an account using the factory + // ```js + { + // create the entrypoint and the account management factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); + + // create the account to guard // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // we can use a trusted service that provides a guardian, or simply set another address we own or trust // md-as-comment + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await factory.createTransactionForSettingGuardian(alice.address, { + guardianAddress: guardian, + serviceID: "SelfOwnedAddress", // this is just an example // md-as-comment + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // Once a guardian is set, we must wait **20 epochs** before it can be activated. After activation, all transactions sent from the account must also be signed by the guardian. + + // #### Activating the guardian using the controller + // ```js + { + // create the entrypoint and the account controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); + + // create the account to guard // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForGuardingAccount( + alice, + alice.getNonceThenIncrement(), + {}, + ); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Activating the guardian using the factory + // ```js + { + // create the entrypoint and the account factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); + + // create the account to guard // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForGuardingAccount(alice.address); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Unguarding the account using the controller + // ```js + { + // create the entrypoint and the account controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); + + // create the account to unguard // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForUnguardingAccount( + alice, + alice.getNonceThenIncrement(), + {}, + ); + + // the transaction should also be signed by the guardian before being sent otherwise it won't be executed // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Unguarding the guardian using the factory + // ```js + { + // create the entrypoint and the account factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); + + // create the account to guard // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForUnguardingAccount(alice.address); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Saving a key-value pair to an account using the controller + // You can find more information [here](https://docs.multiversx.com/developers/account-storage) regarding the account storage. + + // ```js + { + // create the entrypoint and the account controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // creating the key-value pairs we want to save // md-as-comment + const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForSavingKeyValue(alice, alice.getNonceThenIncrement(), { + keyValuePairs: keyValuePairs, + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Saving a key-value pair to an account using the factory + // ```js + { + // create the entrypoint and the account factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); + + // create the account to guard // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // creating the key-value pairs we want to save // md-as-comment + const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); + + const transaction = await factory.createTransactionForSavingKeyValue(alice.address, { + keyValuePairs: keyValuePairs, + }); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/addresses.js b/cookbook/addresses.js deleted file mode 100644 index 55b39d1e..00000000 --- a/cookbook/addresses.js +++ /dev/null @@ -1,85 +0,0 @@ -import { Address, AddressComputer, AddressFactory, LibraryConfig } from "@multiversx/sdk-core"; // md-ignore - -// ## Addresses - -// Create an `Address` object from a bech32-encoded string: - -// ``` js -{ - // Create an Address object from a bech32-encoded string // md-as-comment - const address = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - - console.log("Address (bech32-encoded):", address.toBech32()); - console.log("Public key (hex-encoded):", address.toHex()); - console.log("Public key (hex-encoded):", Buffer.from(address.getPublicKey()).toString("hex")); -} - -// ``` - -// Here’s how you can create an address from a hex-encoded string using the MultiversX JavaScript SDK: -// If the HRP (human-readable part) is not provided, the SDK will use the default one ("erd"). - -// ``` js -{ - // Create an address from a hex-encoded string with a specified HRP // md-as-comment - const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "erd"); - - console.log("Address (bech32-encoded):", address.toBech32()); - console.log("Public key (hex-encoded):", address.toHex()); -} -// ``` - -// #### Create an address from a raw public key - -// ``` js -{ - const pubkey = Buffer.from("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "hex"); - const addressFromPubkey = new Address(pubkey, "erd"); -} -// ``` - -// #### Using an AddressFactory to create addresses -// AddressFactory allows creating addresses with a custom HRP, ensuring consistency across your application. - -// ``` js -{ - const factory = new AddressFactory("erd"); - - const address1 = factory.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const address2 = factory.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); - const address3 = factory.fromPublicKey(Buffer.from("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "hex")); -} -// ``` - -// #### Getting the shard of an address -// ``` js - -const addressComputer = new AddressComputer(); -console.log("Shard:", addressComputer.getShardOfAddress(address)); -// ``` - -// Checking if an address is a smart contract -// ``` js - -const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm"); -console.log("Is contract address:", contractAddress.isSmartContract()); -// ``` - -// ### Changing the default hrp -// The **LibraryConfig** class manages the default **HRP** (human-readable part) for addresses, which is set to `"erd"` by default. -// You can change the HRP when creating an address or modify it globally in **LibraryConfig**, affecting all newly created addresses. -// ``` js - -console.log(LibraryConfig.defaultAddressHrp); -const defaultAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); -console.log(defaultAddress.toBech32()); - -LibraryConfig.defaultAddressHrp = "test"; -const testAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); -console.log(testAddress.toBech32()); - -// Reset HRP back to "erd" to avoid affecting other parts of the application. // md-as-comment -LibraryConfig.defaultAddressHrp = "erd"; -// ``` - - diff --git a/cookbook/addresses.ts b/cookbook/addresses.ts new file mode 100644 index 00000000..2c49cb8f --- /dev/null +++ b/cookbook/addresses.ts @@ -0,0 +1,74 @@ +import { Address, AddressComputer, LibraryConfig } from "../src"; // md-ignore +(async () => { + // ## Addresses + + // Create an `Address` object from a bech32-encoded string: + + // ``` js + { + // Create an Address object from a bech32-encoded string // md-as-comment + const address = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + + console.log("Address (bech32-encoded):", address.toBech32()); + console.log("Public key (hex-encoded):", address.toHex()); + console.log("Public key (hex-encoded):", Buffer.from(address.getPublicKey()).toString("hex")); + } + + // ``` + + // Here’s how you can create an address from a hex-encoded string using the MultiversX JavaScript SDK: + // If the HRP (human-readable part) is not provided, the SDK will use the default one ("erd"). + + // ``` js + { + // Create an address from a hex-encoded string with a specified HRP // md-as-comment + const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "erd"); + + console.log("Address (bech32-encoded):", address.toBech32()); + console.log("Public key (hex-encoded):", address.toHex()); + } + // ``` + + // #### Create an address from a raw public key + + // ``` js + { + const pubkey = Buffer.from("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "hex"); + const addressFromPubkey = new Address(pubkey, "erd"); + } + // ``` + + // #### Getting the shard of an address + // ``` js + + const addressComputer = new AddressComputer(); + const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); + console.log("Shard:", addressComputer.getShardOfAddress(address)); + // ``` + + // Checking if an address is a smart contract + // ``` js + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm"); + console.log("Is contract address:", contractAddress.isSmartContract()); + // ``` + + // ### Changing the default hrp + // The **LibraryConfig** class manages the default **HRP** (human-readable part) for addresses, which is set to `"erd"` by default. + // You can change the HRP when creating an address or modify it globally in **LibraryConfig**, affecting all newly created addresses. + // ``` js + + console.log(LibraryConfig.DefaultAddressHrp); + const defaultAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); + console.log(defaultAddress.toBech32()); + + LibraryConfig.DefaultAddressHrp = "test"; + const testAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); + console.log(testAddress.toBech32()); + + // Reset HRP back to "erd" to avoid affecting other parts of the application. // md-as-comment + LibraryConfig.DefaultAddressHrp = "erd"; + // ``` +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/cookbook.md b/cookbook/cookbook.md index 844d154e..b1d25238 100644 --- a/cookbook/cookbook.md +++ b/cookbook/cookbook.md @@ -1,3168 +1,3190 @@ -## Overview +(async () => { + // ## Overview -This guide walks you through handling common tasks using the MultiversX Javascript SDK (v14, latest stable version). + // This guide walks you through handling common tasks using the MultiversX Javascript SDK (v14, latest stable version). -## Creating an Entrypoint + // ## Creating an Entrypoint -An Entrypoint represents a network client that simplifies access to the most common operations. -There is a dedicated entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint`, `LocalnetEntrypoint`. + // An Entrypoint represents a network client that simplifies access to the most common operations. + // There is a dedicated entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint`, `LocalnetEntrypoint`. -For example, to create a Devnet entrypoint you have the following command: + // For example, to create a Devnet entrypoint you have the following command: -```js -const entrypoint = new DevnetEntrypoint(); -``` - -#### Using a Custom API -If you'd like to connect to a third-party API, you can specify the url parameter: + // ```js + const entrypoint = new DevnetEntrypoint(); + // ``` -```js -const apiEntrypoint = new DevnetEntrypoint({ url: 'https://custom-multiversx-devnet-api.com' }); -``` + // #### Using a Custom API + // If you'd like to connect to a third-party API, you can specify the url parameter: -#### Using a Proxy + // ```js + const apiEntrypoint = new DevnetEntrypoint("https://custom-multiversx-devnet-api.com"); + // ``` -By default, the DevnetEntrypoint uses the standard API. However, you can create a custom entrypoint that interacts with a proxy by specifying the kind parameter: + // #### Using a Proxy + // By default, the DevnetEntrypoint uses the standard API. However, you can create a custom entrypoint that interacts with a proxy by specifying the kind parameter: -```js -const customEntrypoint = new DevnetEntrypoint({ - url: 'https://devnet-gateway.multiversx.com', - kind: 'proxy' + // ```js + const customEntrypoint = new DevnetEntrypoint("https://devnet-gateway.multiversx.com", "proxy"); + // ``` +})().catch((e) => { + console.log({ e }); }); -``` -## Creating Accounts +(async () => { + // ## Creating Accounts -You can initialize an account directly from the entrypoint. Keep in mind that the account is network agnostic, meaning it doesn't matter which entrypoint is used. -Accounts are used for signing transactions and messages and managing the account's nonce. They can also be saved to a PEM or keystore file for future use. + // You can initialize an account directly from the entrypoint. Keep in mind that the account is network agnostic, meaning it doesn't matter which entrypoint is used. + // Accounts are used for signing transactions and messages and managing the account's nonce. They can also be saved to a PEM or keystore file for future use. -```js -{ - const entrypoint = new DevnetEntrypoint(); - const account = entrypoint.createAccount(); -} -``` + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const account = entrypoint.createAccount(); + } + // ``` -### Other Ways to Instantiate an Account + // ### Other Ways to Instantiate an Account -#### From a Secret Key -```js -{ - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); + // #### From a Secret Key + // ```js + { + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); - const accountFromSecretKey = new Account(secretKey); -} -``` + const accountFromSecretKey = new Account(secretKey); + } + // ``` -#### From a PEM file -```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const accountFromPem = Account.newFromPem(filePath); -} -``` + // #### From a PEM file + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const accountFromPem = Account.newFromPem(filePath); + } + // ``` -#### From a Keystore File -```js -{ - const keystorePath = path.join("src", "testdata", "testwallets", "alice.json"); - const accountFromKeystore = Account.newFromKeystore({ - filePath: keystorePath, - password: "password" - }); -} -``` + // #### From a Keystore File + // ```js + { + const keystorePath = path.join("../src", "testdata", "testwallets", "alice.json"); + const accountFromKeystore = Account.newFromKeystore(keystorePath, "password"); + } + // ``` -#### From a Mnemonic -```js + // #### From a Mnemonic + // ```js -const mnemonic = Mnemonic.generate(); -const accountFromMnemonic = Account.newFromMnemonic(mnemonic.getText()); -``` + const mnemonic = Mnemonic.generate(); + const accountFromMnemonic = Account.newFromMnemonic(mnemonic.toString()); + // ``` -#### From a KeyPair + // #### From a KeyPair -```js -const keypair = KeyPair.generate(); -const accountFromKeyPairs = Account.newFromKeypair(keypair); -``` + // ```js + const keypair = KeyPair.generate(); + const accountFromKeyPairs = Account.newFromKeypair(keypair); + // ``` -### Managing the Account Nonce + // ### Managing the Account Nonce -An account has a `nonce` property that the user is responsible for managing. -You can fetch the nonce from the network and increment it after each transaction. -Each transaction must have the correct nonce, otherwise it will fail to execute. + // An account has a `nonce` property that the user is responsible for managing. + // You can fetch the nonce from the network and increment it after each transaction. + // Each transaction must have the correct nonce, otherwise it will fail to execute. -```js -{ - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const key = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); + // ```js + { + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const key = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); - const accountWithNonce = new Account(key); - const entrypoint = new DevnetEntrypoint(); + const accountWithNonce = new Account(key); + const entrypoint = new DevnetEntrypoint(); - // Fetch the current nonce from the network - accountWithNonce.nonce = await entrypoint.recallAccountNonce(accountWithNonce.address); + // Fetch the current nonce from the network + accountWithNonce.nonce = await entrypoint.recallAccountNonce(accountWithNonce.address); - // Create and send a transaction here... + // Create and send a transaction here... - // Increment nonce after each transaction - const nonce = accountWithNonce.getNonceThenIncrement(); -} -``` + // Increment nonce after each transaction + const nonce = accountWithNonce.getNonceThenIncrement(); + } + // ``` -For more details, see the [Creating Transactions](#creating-transactions) section. + // For more details, see the [Creating Transactions](#creating-transactions) section. -#### Saving the Account to a File + // #### Saving the Account to a File -Accounts can be saved to either a PEM file or a keystore file. -While PEM wallets are less secure for storing secret keys, they are convenient for testing purposes. -Keystore files offer a higher level of security. + // Accounts can be saved to either a PEM file or a keystore file. + // While PEM wallets are less secure for storing secret keys, they are convenient for testing purposes. + // Keystore files offer a higher level of security. -#### Saving the Account to a PEM File -```js -{ - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); + // #### Saving the Account to a PEM File + // ```js + { + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); - const account = new Account(secretKey); - account.saveToPem({ path: path.resolve("wallet.pem") }); -} -``` + const account = new Account(secretKey); + account.saveToPem(path.resolve("wallet.pem")); + } + // ``` -#### Saving the Account to a Keystore File -```js -{ - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, 'hex')); + // #### Saving the Account to a Keystore File + // ```js + { + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); - const account = new Account(secretKey); - account.saveToKeystore({ - path: path.resolve("keystoreWallet.json"), - password: "password" - }); -} + const account = new Account(secretKey); + account.saveToKeystore(path.resolve("keystoreWallet.json"), "password"); + } -``` + // ``` -### Using a Ledger Device + // ### Using a Ledger Device -You can manage your account with a Ledger device, allowing you to sign both transactions and messages while keeping your keys secure. + // You can manage your account with a Ledger device, allowing you to sign both transactions and messages while keeping your keys secure. -Note: **The multiversx-sdk package does not include Ledger support by default. To enable it, install the package with Ledger dependencies**: + // Note: **The multiversx-sdk package does not include Ledger support by default. To enable it, install the package with Ledger dependencies**: ```bash npm install @multiversx/sdk-hw-provider ``` -#### Creating a Ledger Account -This can be done using the dedicated library. You can find more information [here](/sdk-and-tools/sdk-js/sdk-js-signing-providers/#the-hardware-wallet-provider). - -When signing transactions or messages, the Ledger device will prompt you to confirm the details before proceeding. - -### Compatibility with IAccount Interface + // #### Creating a Ledger Account + // This can be done using the dedicated library. You can find more information [here](/sdk-and-tools/sdk-js/sdk-js-signing-providers/#the-hardware-wallet-provider). -The `Account` implements the `IAccount` interface, making it compatible with transaction controllers and any other component that expects this interface. + // When signing transactions or messages, the Ledger device will prompt you to confirm the details before proceeding. -## Calling the Faucet + // ### Compatibility with IAccount Interface -This functionality is not yet available through the entrypoint, but we recommend using the faucet available within the Web Wallet. For more details about the faucet [see this](/wallet/web-wallet/#testnet-and-devnet-faucet). + // The `Account` implements the `IAccount` interface, making it compatible with transaction controllers and any other component that expects this interface. +})().catch((e) => { + console.log({ e }); +}); -- [Testnet Wallet](https://testnet-wallet.multiversx.com/). -- [Devnet Wallet](https://devnet-wallet.multiversx.com/). +import { + Address, + ApiNetworkProvider, + DevnetEntrypoint, + ProxyNetworkProvider, + SmartContractQuery, + Token, + Transaction, +(async () => { + // ## Calling the Faucet -### Interacting with the network + // This functionality is not yet available through the entrypoint, but we recommend using the faucet available within the Web Wallet. For more details about the faucet [see this](/wallet/web-wallet/#testnet-and-devnet-faucet). -The entrypoint exposes a few ways to directly interact with the network, such as: + // - [Testnet Wallet](https://testnet-wallet.multiversx.com/). + // - [Devnet Wallet](https://devnet-wallet.multiversx.com/). -- `recallAccountNonce(address: Address): Promise;` -- `sendTransactions(transactions: Transaction[]): Promise<[number, string[]]>;` -- `sendTransaction(transaction: Transaction): Promise;` -- `getTransaction(txHash: string): Promise;` -- `awaitCompletedTransaction(txHash: string): Promise;` + // ### Interacting with the network -Some other methods are exposed through a so called **network provider**. + // The entrypoint exposes a few ways to directly interact with the network, such as: -- **ApiNetworkProvider**: Interacts with the API, which is a layer over the proxy. It fetches data from the network and `Elastic Search`. -- **ProxyNetworkProvider**: Interacts directly with the proxy of an observing squad. + // - `recallAccountNonce(address: Address): Promise;` + // - `sendTransactions(transactions: Transaction[]): Promise<[number, string[]]>;` + // - `sendTransaction(transaction: Transaction): Promise;` + // - `getTransaction(txHash: string): Promise;` + // - `awaitCompletedTransaction(txHash: string): Promise;` -To get the underlying network provider from our entrypoint, we can do as follows: + // Some other methods are exposed through a so called **network provider**. -```js -{ - const entrypoint = new DevnetEntrypoint(); - const networkProvider = entrypoint.createNetworkProvider(); -} -``` + // - **ApiNetworkProvider**: Interacts with the API, which is a layer over the proxy. It fetches data from the network and `Elastic Search`. + // - **ProxyNetworkProvider**: Interacts directly with the proxy of an observing squad. -### Creating a network provider -When manually instantiating a network provider, you can provide a configuration to specify the client name and set custom request options. - -```js -{ - // Create a configuration object - const config = { - clientName: "hello-multiversx", - requestsOptions: { - timeout: 1000, // Timeout in milliseconds - auth: { - username: "user", - password: "password" - } - } - }; - - // Instantiate the network provider with the config - const api = new ApiNetworkProvider("https://devnet-api.multiversx.com", config); -} -``` + // To get the underlying network provider from our entrypoint, we can do as follows: -A full list of available methods for `ApiNetworkProvider` can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/classes/ApiNetworkProvider.html). + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); + } + // ``` -Both `ApiNetworkProvider` and `ProxyNetworkProvider` implement a common interface, which can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/interfaces/INetworkProvider.html). This allows them to be used interchangeably. + // ### Creating a network provider + // When manually instantiating a network provider, you can provide a configuration to specify the client name and set custom request options. -The classes returned by the API expose the most commonly used fields directly for convenience. However, each object also contains a `raw` field that stores the original API response, allowing access to additional fields if needed. + // ```js + { + // Create a configuration object + const config = { + clientName: "hello-multiversx", + requestsOptions: { + timeout: 1000, // Timeout in milliseconds + auth: { + username: "user", + password: "password", + }, + }, + }; + + // Instantiate the network provider with the config + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com", config); + } + // ``` + // A full list of available methods for `ApiNetworkProvider` can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/classes/ApiNetworkProvider.html). -## Fetching data from the network + // Both `ApiNetworkProvider` and `ProxyNetworkProvider` implement a common interface, which can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/interfaces/INetworkProvider.html). This allows them to be used interchangeably. -### Fetching the network config + // The classes returned by the API expose the most commonly used fields directly for convenience. However, each object also contains a `raw` field that stores the original API response, allowing access to additional fields if needed. -```js -{ - const entrypoint = new DevnetEntrypoint(); - const networkProvider = entrypoint.createNetworkProvider(); + // ## Fetching data from the network - const networkConfig = entrypoint.getNetworkConfig(); -} -``` + // ### Fetching the network config -### Fetching the network status + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); -```js -{ - const entrypoint = new DevnetEntrypoint(); - const networkProvider = entrypoint.createNetworkProvider(); + const networkConfig = networkProvider.getNetworkConfig(); + } + // ``` - const metaNetworkStatus = entrypoint.getNetworkStatus(); // fetches status from metachain - const networkStatus = entrypoint.getNetworkStatus(1); // fetches status from shard one -} -``` + // ### Fetching the network status -### Fetching a Block from the Network -To fetch a block, we first instantiate the required arguments and use its hash. The API only supports fetching blocks by hash, whereas the **PROXY** allows fetching blocks by either hash or nonce. + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); -When using the **PROXY**, keep in mind that the shard must also be specified in the arguments. + const metaNetworkStatus = networkProvider.getNetworkStatus(); // fetches status from metachain + const networkStatus = networkProvider.getNetworkStatus(); // fetches status from shard one + } + // ``` -#### Fetching a block using the **API** -```js -{ - const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); - const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; - const block = await api.getBlock(blockHash); -} -``` + // ### Fetching a Block from the Network + // To fetch a block, we first instantiate the required arguments and use its hash. The API only supports fetching blocks by hash, whereas the **PROXY** allows fetching blocks by either hash or nonce. -Additionally, we can fetch the latest block from the network: + // When using the **PROXY**, keep in mind that the shard must also be specified in the arguments. -```js -{ - const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); - const latestBlock = await api.getLatestBlock(); -} -``` + // #### Fetching a block using the **API** + // ```js + { + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); + const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; + const block = await api.getBlock(blockHash); + } + // ``` -#### Fetching a block using the **PROXY** + // Additionally, we can fetch the latest block from the network: -When using the proxy, we have to provide the shard, as well. -```js -{ - const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); - const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; - const block = proxy.getBlock({ blockHash, shard: 1 }); -} -``` + // ```js + { + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); + const latestBlock = await api.getLatestBlock(); + } + // ``` -We can also fetch the latest block from the network. -By default, the shard will be the metachain, but we can specify a different shard if needed. + // #### Fetching a block using the **PROXY** -```js -{ - const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); - const latestBlock = proxy.getLatestBlock(); -} -``` + // When using the proxy, we have to provide the shard, as well. + // ```js + { + const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); + const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; + const block = proxy.getBlock({ blockHash, shard: 1 }); + } + // ``` -### Fetching an Account -To fetch an account, we need its address. Once we have the address, we create an `Address` object and pass it as an argument to the method. + // We can also fetch the latest block from the network. + // By default, the shard will be the metachain, but we can specify a different shard if needed. -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const account = await api.getAccount(alice); -} -``` + // ```js + { + const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); + const latestBlock = proxy.getLatestBlock(); + } + // ``` -### Fetching an Account's Storage -We can also fetch an account's storage, allowing us to retrieve all key-value pairs saved for that account. + // ### Fetching an Account + // To fetch an account, we need its address. Once we have the address, we create an `Address` object and pass it as an argument to the method. -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const account = await api.getAccountStorage(alice); -} -``` + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccount(alice); + } + // ``` -If we only want to fetch a specific key, we can do so as follows: + // ### Fetching an Account's Storage + // We can also fetch an account's storage, allowing us to retrieve all key-value pairs saved for that account. -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const account = await api.getAccountStorageEntry(alice, "testKey"); -} -``` + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccountStorage(alice); + } + // ``` -### Waiting for an Account to Meet a Condition -There are times when we need to wait for a specific condition to be met before proceeding with an action. -For example, let's say we want to send 7 EGLD from Alice to Bob, but this can only happen once Alice's balance reaches at least 7 EGLD. -This approach is useful in scenarios where you're waiting for external funds to be sent to Alice, enabling her to transfer the required amount to another recipient. - -To implement this, we need to define the condition to check each time the account is fetched from the network. We create a function that takes an `AccountOnNetwork` object as an argument and returns a `bool`. -Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. - -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const condition = (account) => { - return account.balance >= 7000000000000000000; // 7 EGLD - }; - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const account = await api.awaitAccountOnCondition(alice, condition); -} -``` + // If we only want to fetch a specific key, we can do so as follows: -### Sending and Simulating Transactions -To execute transactions, we use the network providers to broadcast them to the network. Keep in mind that for transactions to be processed, they must be signed. + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccountStorageEntry(alice, "testKey"); + } + // ``` -#### Sending a Transaction + // ### Waiting for an Account to Meet a Condition + // There are times when we need to wait for a specific condition to be met before proceeding with an action. + // For example, let's say we want to send 7 EGLD from Alice to Bob, but this can only happen once Alice's balance reaches at least 7 EGLD. + // This approach is useful in scenarios where you're waiting for external funds to be sent to Alice, enabling her to transfer the required amount to another recipient. -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); + // To implement this, we need to define the condition to check each time the account is fetched from the network. We create a function that takes an `AccountOnNetwork` object as an argument and returns a `bool`. + // Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const condition = (account: any) => { + return account.balance >= 7000000000000000000n; // 7 EGLD + }; + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.awaitAccountOnCondition(alice, condition); + } + // ``` - const transaction = new Transaction({ - sender: alice, - receiver: bob, - gasLimit: 50000n, - chainID: "D", - }); + // ### Sending and Simulating Transactions + // To execute transactions, we use the network providers to broadcast them to the network. Keep in mind that for transactions to be processed, they must be signed. - // set the correct nonce and sign the transaction ... + // #### Sending a Transaction - const transactionHash = await api.sendTransaction(transaction); -} -``` + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); -#### Sending multiple transactions -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - const firstTransaction = new Transaction({ - sender: alice, - receiver: bob, - gasLimit: 50000n, - chainID: "D", - nonce: 2 - }); - - const secondTransaction = new Transaction({ - sender: bob, - receiver: alice, - gasLimit: 50000n, - chainID: "D", - nonce: 1, - }); - - const thirdTransaction = new Transaction({ - sender: alice, - receiver: alice, - gasLimit: 60000n, - chainID: "D", - nonce: 3, - data: new Uint8Array(Buffer.from("hello")) - }); - - // set the correct nonce and sign the transaction ... - - const [numOfSentTxs, hashes] = await api.sendTransactions([firstTransaction, secondTransaction, thirdTransaction]); -} -``` + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); -#### Simulating transactions -A transaction can be simulated before being sent for processing by the network. This is primarily used for smart contract calls, allowing you to preview the results produced by the smart contract. + const transaction = new Transaction({ + sender: alice, + receiver: bob, + gasLimit: 50000n, + chainID: "D", + }); -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); + // set the correct nonce and sign the transaction ... - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); + const transactionHash = await api.sendTransaction(transaction); + } + // ``` - const transaction = new Transaction({ - sender: alice, - receiver: contract, - gasLimit: 5000000n, - chainID: "D", - data: new Uint8Array(Buffer.from("add@07")) - }); + // #### Sending multiple transactions + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const firstTransaction = new Transaction({ + sender: alice, + receiver: bob, + gasLimit: 50000n, + chainID: "D", + nonce: 2n, + }); + + const secondTransaction = new Transaction({ + sender: bob, + receiver: alice, + gasLimit: 50000n, + chainID: "D", + nonce: 1n, + }); + + const thirdTransaction = new Transaction({ + sender: alice, + receiver: alice, + gasLimit: 60000n, + chainID: "D", + nonce: 3n, + data: new Uint8Array(Buffer.from("hello")), + }); + + // set the correct nonce and sign the transaction ... + + const [numOfSentTxs, hashes] = await api.sendTransactions([ + firstTransaction, + secondTransaction, + thirdTransaction, + ]); + } + // ``` - const transactionOnNetwork = await api.simulateTransaction(transaction); -} -``` + // #### Simulating transactions + // A transaction can be simulated before being sent for processing by the network. This is primarily used for smart contract calls, allowing you to preview the results produced by the smart contract. -#### Estimating the gas cost of a transaction -Before sending a transaction to the network for processing, you can retrieve the estimated gas limit required for the transaction to be executed. + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); + const transaction = new Transaction({ + sender: alice, + receiver: contract, + gasLimit: 5000000n, + chainID: "D", + data: new Uint8Array(Buffer.from("add@07")), + }); - const nonce = await entrypoint.recallAccountNonce(alice); + const transactionOnNetwork = await api.simulateTransaction(transaction); + } + // ``` - const transaction = new Transaction({ - sender: alice, - receiver: contract, - gasLimit: 5000000, - chainID: "D", - data: new Uint8Array(Buffer.from("add@07")), - nonce: nonce - }); + // #### Estimating the gas cost of a transaction + // Before sending a transaction to the network for processing, you can retrieve the estimated gas limit required for the transaction to be executed. - const transactionCostResponse = await api.estimateTransactionCost(transaction); -} -``` + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); -### Waiting for transaction completion -After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); + const nonce = await entrypoint.recallAccountNonce(alice); - const txHash = "exampletransactionhash"; - const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); -} -``` + const transaction = new Transaction({ + sender: alice, + receiver: contract, + gasLimit: 5000000n, + chainID: "D", + data: new Uint8Array(Buffer.from("add@07")), + nonce: nonce, + }); -### Waiting for a Transaction to Satisfy a Condition -Similar to accounts, we can wait until a transaction meets a specific condition. + const transactionCostResponse = await api.estimateTransactionCost(transaction); + } + // ``` -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); + // ### Waiting for transaction completion + // After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. - const condition = (txOnNetwork) => !txOnNetwork.status.isSuccessful(); + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const txHash = "exampletransactionhash"; - const transactionOnNetwork = await api.awaitTransactionOnCondition(txHash, condition); -} -``` + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); + } + // ``` -### Waiting for transaction completion -After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. + // ### Waiting for a Transaction to Satisfy a Condition + // Similar to accounts, we can wait until a transaction meets a specific condition. -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const txHash = "exampletransactionhash"; - const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); -} -``` + const condition = (txOnNetwork: any) => !txOnNetwork.status.isSuccessful(); -### Fetching Transactions from the Network -After sending a transaction, we can fetch it from the network using the transaction hash, which we receive after broadcasting the transaction. + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionOnCondition(txHash, condition); + } + // ``` -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); + // ### Waiting for transaction completion + // After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. - const txHash = "exampletransactionhash"; - const transactionOnNetwork = await api.getTransaction(txHash); -} -``` + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); -### Fetching a token from an account -We can fetch a specific token (ESDT, MetaESDT, SFT, NFT) from an account by providing the account's address and the token identifier. + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); + } + // ``` -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); + // ### Fetching Transactions from the Network + // After sending a transaction, we can fetch it from the network using the transaction hash, which we receive after broadcasting the transaction. - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - let token = new Token({ identifier: "TEST-ff155e" }); // ESDT - let tokenOnNetwork = await api.getTokenOfAccount(alice, token); + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.getTransaction(txHash); + } + // ``` - token = new Token({ identifier: "NFT-987654", nonce: 11n }); // NFT - tokenOnNetwork = await api.getTokenOfAccount(alice, token); -} -``` + // ### Fetching a token from an account + // We can fetch a specific token (ESDT, MetaESDT, SFT, NFT) from an account by providing the account's address and the token identifier. -### Fetching all fungible tokens of an account -Fetches all fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + let token = new Token({ identifier: "TEST-ff155e" }); // ESDT + let tokenOnNetwork = await api.getTokenOfAccount(alice, token); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const fungibleTokens = await api.getFungibleTokensOfAccount(alice); -} -``` + token = new Token({ identifier: "NFT-987654", nonce: 11n }); // NFT + tokenOnNetwork = await api.getTokenOfAccount(alice, token); + } + // ``` -### Fetching all non-fungible tokens of an account -Fetches all non-fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. + // ### Fetching all fungible tokens of an account + // Fetches all fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const nfts = await api.getNonFungibleTokensOfAccount(alice); -} -``` + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const fungibleTokens = await api.getFungibleTokensOfAccount(alice); + } + // ``` -### Fetching token metadata -If we want to fetch the metadata of a token (e.g., owner, decimals, etc.), we can use the following methods: + // ### Fetching all non-fungible tokens of an account + // Fetches all non-fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - // used for ESDT - const fungibleTokenDefinition = await api.getDefinitionOfFungibleToken("TEST-ff155e"); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const nfts = await api.getNonFungibleTokensOfAccount(alice); + } + // ``` - // used for METAESDT, SFT, NFT - const nonFungibleTokenDefinition = await api.getDefinitionOfTokenCollection("NFTEST-ec88b8"); -} -``` + // ### Fetching token metadata + // If we want to fetch the metadata of a token (e.g., owner, decimals, etc.), we can use the following methods: -### Querying Smart Contracts -Smart contract queries, or view functions, are endpoints that only read data from the contract. To send a query to the observer nodes, we can proceed as follows: - -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const query = new SmartContractQuery({ - contract: Address.newFromBech32("erd1qqqqqqqqqqqqqpgqqy34h7he2ya6qcagqre7ur7cc65vt0mxrc8qnudkr4"), - function: "getSum", - arguments: [], - }); - const response = await api.queryContract(query); -} -``` + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); -### Custom Api/Proxy calls -The methods exposed by the `ApiNetworkProvider` or `ProxyNetworkProvider` are the most common and widely used. However, there may be times when custom API calls are needed. For these cases, we’ve created generic methods for both GET and POST requests. -Let’s assume we want to retrieve all the transactions sent by Alice in which the `delegate` function was called. + // used for ESDT + const fungibleTokenDefinition = await api.getDefinitionOfFungibleToken("TEST-ff155e"); -```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); + // used for METAESDT, SFT, NFT + const nonFungibleTokenDefinition = await api.getDefinitionOfTokenCollection("NFTEST-ec88b8"); + } + // ``` - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const url = `transactions/${alice.toBech32()}?function=delegate`; + // ### Querying Smart Contracts + // Smart contract queries, or view functions, are endpoints that only read data from the contract. To send a query to the observer nodes, we can proceed as follows: - const response = await api.doGetGeneric(url); -} -``` + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const query = new SmartContractQuery({ + contract: Address.newFromBech32("erd1qqqqqqqqqqqqqpgqqy34h7he2ya6qcagqre7ur7cc65vt0mxrc8qnudkr4"), + function: "getSum", + arguments: [], + }); + const response = await api.queryContract(query); + } + // ``` -## Creating transactions + // ### Custom Api/Proxy calls + // The methods exposed by the `ApiNetworkProvider` or `ProxyNetworkProvider` are the most common and widely used. However, there may be times when custom API calls are needed. For these cases, we’ve created generic methods for both GET and POST requests. + // Let’s assume we want to retrieve all the transactions sent by Alice in which the `delegate` function was called. -In this section, we’ll explore how to create different types of transactions. To create transactions, we can use either controllers or factories. -Controllers are ideal for quick scripts or network interactions, while factories provide a more granular and lower-level approach, typically required for DApps. + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); -Controllers typically use the same parameters as factories, but they also require an Account object and the sender’s nonce. -Controllers also include extra functionality, such as waiting for transaction completion and parsing transactions. -The same functionality can be achieved for transactions built using factories, and we’ll see how in the sections below. In the next section, we’ll learn how to create transactions using both methods. + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const url = `transactions/${alice.toBech32()}?function=delegate`; -### Instantiating Controllers and Factories -There are two ways to create controllers and factories: -1. Get them from the entrypoint. -2. Manually instantiate them. + const response = await api.doGetGeneric(url); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); -```js -{ - const entrypoint = new DevnetEntrypoint(); +import { + Account, + Address, + DevnetEntrypoint, + Token, + TokenTransfer, + TransactionsFactoryConfig, + TransfersController, + TransferTransactionsFactory, +(async () => { + // ## Creating transactions + + // In this section, we’ll explore how to create different types of transactions. To create transactions, we can use either controllers or factories. + // Controllers are ideal for quick scripts or network interactions, while factories provide a more granular and lower-level approach, typically required for DApps. + + // Controllers typically use the same parameters as factories, but they also require an Account object and the sender’s nonce. + // Controllers also include extra functionality, such as waiting for transaction completion and parsing transactions. + // The same functionality can be achieved for transactions built using factories, and we’ll see how in the sections below. In the next section, we’ll learn how to create transactions using both methods. + + // ### Instantiating Controllers and Factories + // There are two ways to create controllers and factories: + // 1. Get them from the entrypoint. + // 2. Manually instantiate them. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); - // getting the controller and the factory from the entrypoint - const transfersController = entrypoint.createTransfersController(); - const transfersFactory = entrypoint.createTransfersTransactionsFactory(); + // getting the controller and the factory from the entrypoint + const transfersController = entrypoint.createTransfersController(); + const transfersFactory = entrypoint.createTransfersTransactionsFactory(); - // manually instantiating the controller and the factory - const controller = new TransfersController({ chainID: 'D' }); + // manually instantiating the controller and the factory + const controller = new TransfersController({ chainID: "D" }); - const config = new TransactionsFactoryConfig({ chainID: 'D' }); - const factory = new TransferTransactionsFactory({ config }); -} -``` + const config = new TransactionsFactoryConfig({ chainID: "D" }); + const factory = new TransferTransactionsFactory({ config }); + } + // ``` -### Token transfers -We can send both native tokens (EGLD) and ESDT tokens using either the controller or the factory. -#### Native Token Transfers Using the Controller -When using the controller, the transaction will be signed because we’ll be working with an Account. + // ### Token transfers + // We can send both native tokens (EGLD) and ESDT tokens using either the controller or the factory. + // #### Native Token Transfers Using the Controller + // When using the controller, the transaction will be signed because we’ll be working with an Account. -```js -{ - const entrypoint = new DevnetEntrypoint(); + // ```js + { + const entrypoint = new DevnetEntrypoint(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transfersController = entrypoint.createTransfersController(); + const transaction = await transfersController.createTransactionForTransfer( + alice, + alice.getNonceThenIncrement(), + { + receiver: bob, + nativeAmount: 1n, + }, + ); + + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + // If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // #### Native Token Transfers Using the Factory + // When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. + // You will need to handle these aspects after the transaction is created. - const transfersController = entrypoint.createTransfersController(); - const transaction = await transfersController.createTransactionForTransfer( - alice, - alice.getNonceThenIncrement(), + // ```js { - receiver: bob, - nativeAmount: 1n, - }, - ); + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTransfersTransactionsFactory(); - const txHash = await entrypoint.sendTransaction(transaction); -} -``` - -If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); -#### Native Token Transfers Using the Factory -When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. -You will need to handle these aspects after the transaction is created. + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -```js -{ - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTransfersTransactionsFactory(); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const transaction = factory.createTransactionForTransfer(alice.address, { + receiver: bob, + nativeAmount: 1000000000000000000n, + }); - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // set the sender's nonce + transaction.nonce = alice.getNonceThenIncrement(); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + // sign the transaction using the sender's account + transaction.signature = await alice.signTransaction(transaction); - const transaction = factory.createTransactionForTransfer(alice, { - receiver: bob, - nativeAmount: 1000000000000000000n, - }); + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - // set the sender's nonce - transaction.nonce = alice.getNonceThenIncrement(); + // If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. - // sign the transaction using the sender's account - transaction.signature = await alice.signTransaction(transaction); + // #### Custom token transfers using the controller - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // ```js + { + const entrypoint = new DevnetEntrypoint(); -If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); -#### Custom token transfers using the controller + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -```js -{ - const entrypoint = new DevnetEntrypoint(); + const esdt = new Token({ identifier: "TEST-123456" }); + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); + const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); - const esdt = new Token({ identifier: "TEST-123456" }); - const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); + const transfersController = entrypoint.createTransfersController(); + const transaction = await transfersController.createTransactionForTransfer( + alice, + alice.getNonceThenIncrement(), + { + receiver: bob, + tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], + }, + ); - const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); - const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); - const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); + // If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. - const transfersController = entrypoint.createTransfersController(); - const transaction = transfersController.createTransactionForTransfer(alice, alice.getNonceThenIncrement(), { - receiver: bob, - tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], - }); + // #### Custom token transfers using the factory + // When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. These aspects should be handled after the transaction is created. - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTransfersTransactionsFactory(); -If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); -#### Custom token transfers using the factory -When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. These aspects should be handled after the transaction is created. + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -```js -{ - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTransfersTransactionsFactory(); + const esdt = new Token({ identifier: "TEST-123456" }); // fungible tokens don't have a nonce + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); // we set the desired amount we want to send - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); // for NFTs we set the amount to `1` - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); + const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); // for SFTs we set the desired amount we want to send - const esdt = new Token({ identifier: "TEST-123456" }); // fungible tokens don't have a nonce - const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); // we set the desired amount we want to send + const transaction = factory.createTransactionForTransfer(alice.address, { + receiver: bob, + tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], + }); - const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); - const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); // for NFTs we set the amount to `1` + // set the sender's nonce + transaction.nonce = alice.getNonceThenIncrement(); - const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); - const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); // for SFTs we set the desired amount we want to send + // sign the transaction using the sender's account + transaction.signature = await alice.signTransaction(transaction); - const transaction = factory.createTransactionForTransfer(alice, { - receiver: bob, - tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], - }); + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - // set the sender's nonce - transaction.nonce = alice.getNonceThenIncrement(); + // If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. - // sign the transaction using the sender's account - transaction.signature = await alice.signTransaction(transaction); + // #### Sending native and custom tokens + // Both native and custom tokens can now be sent. If a `nativeAmount` is provided along with `tokenTransfers`, the native token will be included in the `MultiESDTNFTTransfer` built-in function call. + // We can send both types of tokens using either the `controller` or the `factory`, but for simplicity, we’ll use the controller in this example. - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // ```js + { + const entrypoint = new DevnetEntrypoint(); -If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); -#### Sending native and custom tokens -Both native and custom tokens can now be sent. If a `nativeAmount` is provided along with `tokenTransfers`, the native token will be included in the `MultiESDTNFTTransfer` built-in function call. -We can send both types of tokens using either the `controller` or the `factory`, but for simplicity, we’ll use the controller in this example. + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -```js -{ - const entrypoint = new DevnetEntrypoint(); + const esdt = new Token({ identifier: "TEST-123456" }); + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const transfersController = entrypoint.createTransfersController(); + const transaction = await transfersController.createTransactionForTransfer( + alice, + alice.getNonceThenIncrement(), + { + receiver: bob, + nativeAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer], + }, + ); - const esdt = new Token({ identifier: "TEST-123456" }); - const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); - const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); - const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); +import { + Abi, + Account, + Address, + AddressComputer, + BigUIntValue, + BinaryCodec, + DevnetEntrypoint, + Field, + gatherAllEvents, + SmartContractTransactionsOutcomeParser, + Struct, + Token, + TokenIdentifierValue, + TokenTransfer, + TransactionEventsParser, + U32Value, + U64Value, +import { loadAbiRegistry } from "../src/testutils"; +(async () => { + // ### Smart Contracts + + // #### Contract ABIs + + // A contract's ABI (Application Binary Interface) describes the endpoints, data structures, and events that the contract exposes. + // While interactions with the contract are possible without the ABI, they are much easier to implement when the definitions are available. + + // #### Loading the ABI from a file + // ```js + { + let abiJson = await promises.readFile("../src/testData/adder.abi.json", { encoding: "utf8" }); + let abiObj = JSON.parse(abiJson); + let abi = Abi.create(abiObj); + } + // ``` - const transfersController = entrypoint.createTransfersController(); - const transaction = transfersController.createTransactionForTransfer(alice, alice.getNonceThenIncrement(), { - receiver: bob, - nativeAmount: 1000000000000000000n, - tokenTransfers: [firstTransfer, secondTransfer], - }); + // #### Loading the ABI from an URL - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // ```js + { + const response = await axios.get( + "https://github.com/multiversx/mx-sdk-js-core/raw/main/src/testdata/adder.abi.json", + ); + let abi = Abi.create(response.data); + } + // ``` -### Smart Contracts + // #### Manually construct the ABI -#### Contract ABIs + // If an ABI file isn’t available, but you know the contract’s endpoints and data types, you can manually construct the ABI. -A contract's ABI (Application Binary Interface) describes the endpoints, data structures, and events that the contract exposes. -While interactions with the contract are possible without the ABI, they are much easier to implement when the definitions are available. + // ```js + { + let abi = Abi.create({ + endpoints: [ + { + name: "add", + inputs: [], + outputs: [], + }, + ], + }); + } + // ``` -#### Loading the ABI from a file -```js -{ - let abiJson = await promises.readFile("../contracts/adder.abi.json", { encoding: "utf8" }); - let abiObj = JSON.parse(abiJson); - let abi = AbiRegistry.create(abiObj); -} -``` + // ```js + { + let abi = Abi.create({ + endpoints: [ + { + name: "foo", + inputs: [{ type: "BigUint" }, { type: "u32" }, { type: "Address" }], + outputs: [{ type: "u32" }], + }, + { + name: "bar", + inputs: [{ type: "counted-variadic" }, { type: "variadic" }], + outputs: [], + }, + ], + }); + } + // ``` -#### Loading the ABI from an URL + // ### Smart Contract deployments + // For creating smart contract deployment transactions, we have two options: a controller and a factory. Both function similarly to the ones used for token transfers. + // When creating transactions that interact with smart contracts, it's recommended to provide the ABI file to the controller or factory if possible. + // This allows arguments to be passed as native Javascript values. If the ABI is not available, but we know the expected data types, we can pass arguments as typed values (e.g., `BigUIntValue`, `ListValue`, `StructValue`, etc.) or as raw bytes. -```js -{ - const response = await axios.get("https://github.com/multiversx/mx-sdk-js-core/raw/main/src/testdata/adder.abi.json"); - abi = AbiRegistry.create(response.data); -} -``` + // #### Deploying a Smart Contract Using the Controller -#### Manually construct the ABI + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); -If an ABI file isn’t available, but you know the contract’s endpoints and data types, you can manually construct the ABI. + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); -```js -{ - abi = AbiRegistry.create({ - "endpoints": [{ - "name": "add", - "inputs": [], - "outputs": [] - }] - }); -} -``` + // load the contract bytecode + const bytecode = await promises.readFile("../src/testData/adder.wasm"); + // load the abi file + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); -```js -{ - abi = AbiRegistry.create({ - "endpoints": [ - { - "name": "foo", - "inputs": [ - { "type": "BigUint" }, - { "type": "u32" }, - { "type": "Address" } - ], - "outputs": [ - { "type": "u32" } - ] - }, - { - "name": "bar", - "inputs": [ - { "type": "counted-variadic" }, - { "type": "variadic" } - ], - "outputs": [] - } - ] - }); -} -``` + const controller = entrypoint.createSmartContractController(abi); -### Smart Contract deployments -For creating smart contract deployment transactions, we have two options: a controller and a factory. Both function similarly to the ones used for token transfers. -When creating transactions that interact with smart contracts, it's recommended to provide the ABI file to the controller or factory if possible. -This allows arguments to be passed as native Javascript values. If the ABI is not available, but we know the expected data types, we can pass arguments as typed values (e.g., `BigUIntValue`, `ListValue`, `StructValue`, etc.) or as raw bytes. + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; -#### Deploying a Smart Contract Using the Controller + const deployTransaction = await controller.createTransactionForDeploy(sender, sender.getNonceThenIncrement(), { + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }); -```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const sender = await Account.newFromPem(filePath); - const entrypoint = new DevnetEntrypoint(); + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(deployTransaction); + } + // ``` - // the developer is responsible for managing the nonce - sender.nonce = await entrypoint.recallAccountNonce(sender.address); + // :::tip + // When creating transactions using [`SmartContractController`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/SmartContractController.html) or [`SmartContractTransactionsFactory`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/SmartContractTransactionsFactory.html), even if the ABI is available and provided, + // you can still use [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/TypedValue.html) objects as arguments for deployments and interactions. - // load the contract bytecode - const bytecode = await promises.readFile("../contracts/adder.wasm"); - // load the abi file - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + // Even further, you can use a mix of [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/TypedValue.html) objects and plain JavaScript values and objects. For example: - const controller = entrypoint.createSmartContractController(abi); + // ```js + // let args = [new U32Value(42), "hello", { foo: "bar" }, new TokenIdentifierValue("TEST-abcdef")]; + // ``` + // ::: - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: - let args = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: - args = [42]; + // #### Parsing contract deployment transactions - const deployTransaction = await controller.createTransactionForDeploy( - sender, - sender.getNonceThenIncrement(), + // ```js { - bytecode: bytecode, - gasLimit: 6000000n, - arguments: args, - }, - ); - - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(deployTransaction); -} -``` + // We use the transaction hash we got when broadcasting the transaction -:::tip -When creating transactions using [`SmartContractController`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/SmartContractController.html) or [`SmartContractTransactionsFactory`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/SmartContractTransactionsFactory.html), even if the ABI is available and provided, -you can still use [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/TypedValue.html) objects as arguments for deployments and interactions. - -Even further, you can use a mix of [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/TypedValue.html) objects and plain JavaScript values and objects. For example: + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createSmartContractController(abi); + const outcome = await controller.awaitCompletedDeploy("txHash"); // waits for transaction completion and parses the result + const contractAddress = outcome.contracts[0].address; + } + // ``` -```js -let args = [new U32Value(42), "hello", { foo: "bar" }, new TokenIdentifierValue("TEST-abcdef")]; -``` + // If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: -::: + // ```js + { + // We use the transaction hash we got when broadcasting the transaction + // If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: -#### Parsing contract deployment transactions + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createSmartContractController(); + const networkProvider = entrypoint.createNetworkProvider(); + const transactionOnNetwork = await networkProvider.awaitTransactionCompleted("txHash"); -```js -{ - // We use the transaction hash we got when broadcasting the transaction - const outcome = await controller.awaitCompletedDeploy(txHash); // waits for transaction completion and parses the result - const contractAddress = outcome.contracts[0].address; -} -``` + // parsing the transaction + const outcome = await controller.parseDeploy(transactionOnNetwork); + } + // ``` -If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: + // #### Computing the contract address -```js -{ - // We use the transaction hash we got when broadcasting the transaction - // If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: - const transactionOnNetwork = await controller.awaitTransactionCompleted(txHash); + // Even before broadcasting, at the moment you know the sender's address and the nonce for your deployment transaction, you can (deterministically) compute the (upcoming) address of the smart contract: - // parsing the transaction - const outcome = await controller.parseDeploy(transactionOnNetwork); -} -``` + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createSmartContractTransactionsFactory(); + const bytecode = await promises.readFile("../contracts/adder.wasm"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new BigUIntValue(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const deployTransaction = factory.createTransactionForDeploy(alice.address, { + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }); + const addressComputer = new AddressComputer(); + const contractAddress = addressComputer.computeContractAddress( + deployTransaction.sender, + deployTransaction.nonce, + ); + + console.log("Contract address:", contractAddress.toBech32()); + } + // ``` -#### Computing the contract address + // #### Deploying a Smart Contract using the factory + // After the transaction is created the nonce needs to be properly set and the transaction should be signed before broadcasting it. -Even before broadcasting, at the moment you know the sender's address and the nonce for your deployment transaction, you can (deterministically) compute the (upcoming) address of the smart contract: + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createSmartContractTransactionsFactory(); -```js -{ - const addressComputer = new AddressComputer(); - const contractAddress = addressComputer.computeContractAddress( - deployTransaction.sender, - deployTransaction.nonce - ); + // load the contract bytecode + const bytecode = await promises.readFile("../src/testData/adder.wasm"); - console.log("Contract address:", contractAddress.toBech32()); -} -``` + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new BigUIntValue(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; -#### Deploying a Smart Contract using the factory -After the transaction is created the nonce needs to be properly set and the transaction should be signed before broadcasting it. + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); -```js -{ - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTransfersTransactionsFactory(); + const deployTransaction = await factory.createTransactionForDeploy(alice.address, { + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }); - // load the contract bytecode - const bytecode = await promises.readFile("../contracts/adder.wasm"); + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: - let args = [new BigUIntValue(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: - args = [42]; + // set the nonce + deployTransaction.nonce = alice.nonce; - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // sign the transaction + deployTransaction.signature = await alice.signTransaction(deployTransaction); - const deployTransaction = await factory.createTransactionForDeploy( - sender, - { - bytecode: bytecode, - gasLimit: 6000000n, - arguments: args, - }, - ); + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(deployTransaction); - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(sender.address); + // waiting for transaction to complete + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - // set the nonce - deployTransaction.nonce = alice.nonce; + // parsing transaction + const parser = new SmartContractTransactionsOutcomeParser(); + const parsedOutcome = parser.parseDeploy({ transactionOnNetwork }); + const contractAddress = parsedOutcome.contracts[0].address; - // sign the transaction - deployTransaction.signature = await alice.signTransaction(transaction); + console.log(contractAddress.toBech32()); + } + // ``` - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(deployTransaction); + // ### Smart Contract calls - // waiting for transaction to complete - const transactionOnNetwork = await entrypoint.awaitTransactionCompleted(txHash); + // In this section we'll see how we can call an endpoint of our previously deployed smart contract using both approaches with the `controller` and the `factory`. - // parsing transaction - const parser = new SmartContractTransactionsOutcomeParser(); - const parsedOutcome = parser.parseDeploy(transactionOnNetwork); - const contractAddress = parsedOutcome.contracts[0].address; + // #### Calling a smart contract using the controller - console.log(contractAddress.toBech32()); -} -``` + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); -### Smart Contract calls + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); -In this section we'll see how we can call an endpoint of our previously deployed smart contract using both approaches with the `controller` and the `factory`. + // load the abi file + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + const controller = entrypoint.createSmartContractController(abi); -#### Calling a smart contract using the controller + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); -```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const sender = await Account.newFromPem(filePath); - const entrypoint = new DevnetEntrypoint(); + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; - // the developer is responsible for managing the nonce - sender.nonce = await entrypoint.recallAccountNonce(sender.address); + const transaction = await controller.createTransactionForExecute(sender, sender.getNonceThenIncrement(), { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + }); - // load the abi file - const abi = await loadAbiRegistry("src/testdata/adder.abi.json");; - const controller = entrypoint.createSmartContractController(abi); + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + console.log(txHash); + } + // ``` - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: - let args = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: - args = [42]; + // #### Parsing smart contract call transactions + // In our case, calling the add endpoint does not return anything, but similar to the example above, we could parse this transaction to get the output values of a smart contract call. - const transaction = await controller.createTransactionForExecute( - sender, - sender.getNonceThenIncrement(), + // ```js { - contract: contractAddress, - gasLimit: 5000000n, - function: "add", - arguments: args, - }, - ); + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createSmartContractController(); + const txHash = "b3ae88ad05c464a74db73f4013de05abcfcb4fb6647c67a262a6cfdf330ef4a9"; + // waits for transaction completion and parses the result + const parsedOutcome = await controller.awaitCompletedExecute(txHash); + const values = parsedOutcome.values; + } + // ``` - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(transaction); + // #### Calling a smart contract and sending tokens (transfer & execute) + // Additionally, if an endpoint requires a payment when called, we can send tokens to the contract while creating a smart contract call transaction. + // Both EGLD and ESDT tokens or a combination of both can be sent. This functionality is supported by both the controller and the factory. - console.log(txHash); -} -``` + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); -#### Parsing smart contract call transactions -In our case, calling the add endpoint does not return anything, but similar to the example above, we could parse this transaction to get the output values of a smart contract call. + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); -```js -{ - // waits for transaction completion and parses the result - const parsedOutcome = controller.awaitCompletedExecute(transactionOnNetwork); - const values = parsedOutcome.contracts.values; -} -``` + // load the abi file + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); -#### Calling a smart contract and sending tokens (transfer & execute) -Additionally, if an endpoint requires a payment when called, we can send tokens to the contract while creating a smart contract call transaction. -Both EGLD and ESDT tokens or a combination of both can be sent. This functionality is supported by both the controller and the factory. + // get the smart contracts controller + const controller = entrypoint.createSmartContractController(abi); -```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const sender = await Account.newFromPem(filePath); - const entrypoint = new DevnetEntrypoint(); + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - // the developer is responsible for managing the nonce - sender.nonce = await entrypoint.recallAccountNonce(sender.address); + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; - // load the abi file - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + // creating the transfers + const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10n }); + const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); - // get the smart contracts controller - const controller = entrypoint.createSmartContractController(abi); + const secondToken = new Token({ identifier: "BAR-c80d29" }); + const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + const transaction = await controller.createTransactionForExecute(sender, sender.getNonceThenIncrement(), { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + nativeTransferAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer], + }); - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: - let args = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: - args = [42]; + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); - // creating the transfers - const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10 }); - const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); + console.log(txHash); + } + // ``` - const secondToken = new Token({ identifier: "BAR-c80d29" }); - const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); + // #### Calling a smart contract using the factory + // Let's create the same smart contract call transaction, but using the `factory`. - const transaction = await controller.createTransactionForExecute( - sender, - sender.getNonceThenIncrement(), + // ```js { - contract: contractAddress, - gasLimit: 5000000n, - function: "add", - arguments: args, - nativeTransferAmount: 1000000000000000000n, - tokenTransfers: [firstTransfer, secondTransfer] - }, - ); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(transaction); + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - console.log(txHash); -} -``` + // get the smart contracts controller + const controller = entrypoint.createSmartContractTransactionsFactory(); -#### Calling a smart contract using the factory -Let's create the same smart contract call transaction, but using the `factory`. + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); -```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const entrypoint = new DevnetEntrypoint(); + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // creating the transfers + const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10n }); + const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); - // load the abi file - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + const secondToken = new Token({ identifier: "BAR-c80d29" }); + const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); - // get the smart contracts controller - const controller = entrypoint.createSmartContractTransactionsFactory(abi); + const transaction = await controller.createTransactionForExecute(alice.address, { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + nativeTransferAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer], + }); - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + transaction.nonce = alice.getNonceThenIncrement(); + transaction.signature = await alice.signTransaction(transaction); - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: - let args = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: - args = [42]; + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); - // creating the transfers - const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10 }); - const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); + console.log(txHash); + } + // ``` - const secondToken = new Token({ identifier: "BAR-c80d29" }); - const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); + // #### Parsing transaction outcome + // As said before, the `add` endpoint we called does not return anything, but we could parse the outcome of smart contract call transactions, as follows: - const transaction = await controller.createTransactionForExecute( - sender, + // ```js { - contract: contractAddress, - gasLimit: 5000000n, - function: "add", - arguments: args, - nativeTransferAmount: 1000000000000000000n, - tokenTransfers: [firstTransfer, secondTransfer] - }, - ); - - transaction.nonce = alice.getNonceThenIncrement(); - transaction.signature = await alice.signTransaction(transaction); - - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(transaction); - - console.log(txHash); -} -``` + // load the abi file + const entrypoint = new DevnetEntrypoint(); + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + const parser = new SmartContractTransactionsOutcomeParser({ abi }); + const txHash = "b3ae88ad05c464a74db73f4013de05abcfcb4fb6647c67a262a6cfdf330ef4a9"; + const transactionOnNetwork = await entrypoint.getTransaction(txHash); + const outcome = parser.parseExecute({ transactionOnNetwork }); + } + // ``` -#### Parsing transaction outcome -As said before, the `add` endpoint we called does not return anything, but we could parse the outcome of smart contract call transactions, as follows: - -```js -{ - // load the abi file - const entrypoint = new DevnetEntrypoint(); - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); - const parser = SmartContractTransactionsOutcomeParser({ abi }); - const transactionOnNetwork = entrypoint.getTransaction(txHash); - const outcome = parser.parseExecute(transactionOnNetwork); -} -``` + // #### Decoding transaction events + // You might be interested into decoding events emitted by a contract. You can do so by using the `TransactionEventsParser`. -#### Decoding transaction events -You might be interested into decoding events emitted by a contract. You can do so by using the `TransactionEventsParser`. + // Suppose we'd like to decode a `startPerformAction` event emitted by the [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract. -Suppose we'd like to decode a `startPerformAction` event emitted by the [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract. + // First, we load the abi file, then we fetch the transaction, we extract the event from the transaction and then we parse it. -First, we load the abi file, then we fetch the transaction, we extract the event from the transaction and then we parse it. + // ```js + { + // load the abi files + const entrypoint = new DevnetEntrypoint(); + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + const parser = new TransactionEventsParser({ abi }); + const txHash = "b3ae88ad05c464a74db73f4013de05abcfcb4fb6647c67a262a6cfdf330ef4a9"; + const transactionOnNetwork = await entrypoint.getTransaction(txHash); + const events = gatherAllEvents(transactionOnNetwork); + const outcome = parser.parseEvents({ events }); + } + // ``` -```js -{ - // load the abi files - const entrypoint = new DevnetEntrypoint(); - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); - const parser = new TransactionEventsParser({ abi }); - const transactionOnNetwork = entrypoint.getTransaction(txHash); - const events = gatherAllEvents(transactionOnNetwork); - const outcome = parser.parseEvents({ events }); -} -``` + // #### Decoding transaction events + // Whenever needed, the contract ABI can be used for manually encoding or decoding custom types. -#### Decoding transaction events -Whenever needed, the contract ABI can be used for manually encoding or decoding custom types. + // Let's encode a struct called EsdtTokenPayment (of [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract) into binary data. + // ```js + { + const abi = await loadAbiRegistry("../src/testdata/multisig-full.abi.json"); + const paymentType = abi.getStruct("EsdtTokenPayment"); + const codec = new BinaryCodec(); -Let's encode a struct called EsdtTokenPayment (of [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract) into binary data. -```js -{ - const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); - const paymentType = abi.getStruct("EsdtTokenPayment"); + const paymentStruct = new Struct(paymentType, [ + new Field(new TokenIdentifierValue("TEST-8b028f"), "token_identifier"), + new Field(new U64Value(0n), "token_nonce"), + new Field(new BigUIntValue(10000n), "amount"), + ]); - const paymentStruct = new Struct(paymentType, [ - new Field(new TokenIdentifierValue("TEST-8b028f"), "token_identifier"), - new Field(new U64Value(0n), "token_nonce"), - new Field(new BigUIntValue(10000n), "amount") - ]); + const encoded = codec.encodeNested(paymentStruct); - const encoded = codec.encodeNested(paymentStruct); + console.log(encoded.toString("hex")); + } + // ``` - console.log(encoded.toString("hex")); -} -``` + // Now let's decode a struct using the ABI. + // ```js + { + const abi = await loadAbiRegistry("../src/testdata/multisig-full.abi.json"); + const actionStructType = abi.getEnum("Action"); + const data = Buffer.from( + "0500000000000000000500d006f73c4221216fa679bc559005584c4f1160e569e1000000012a0000000003616464000000010000000107", + "hex", + ); + + const codec = new BinaryCodec(); + const [decoded] = codec.decodeNested(data, actionStructType); + const decodedValue = decoded.valueOf(); + console.log(JSON.stringify(decodedValue, null, 4)); + } + // ``` -Now let's decode a struct using the ABI. -```js -{ - const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); - const actionStructType = abi.getEnum("Action"); - const data = Buffer.from("0500000000000000000500d006f73c4221216fa679bc559005584c4f1160e569e1000000012a0000000003616464000000010000000107", "hex"); - - const [decoded] = codec.decodeNested(data, actionStructType); - const decodedValue = decoded.valueOf(); - console.log(JSON.stringify(decodedValue, null, 4)); -} -``` + // ### Smart Contract queries + // When querying a smart contract, a **view function** is called. A view function does not modify the state of the contract, so we do not need to send a transaction. + // To perform this query, we use the **SmartContractController**. While we can use the contract's ABI file to encode the query arguments, we can also use it to parse the result. + // In this example, we will query the **adder smart contract** by calling its `getSum` endpoint. -### Smart Contract queries -When querying a smart contract, a **view function** is called. A view function does not modify the state of the contract, so we do not need to send a transaction. -To perform this query, we use the **SmartContractController**. While we can use the contract's ABI file to encode the query arguments, we can also use it to parse the result. -In this example, we will query the **adder smart contract** by calling its `getSum` endpoint. + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); -```js -{ - const entrypoint = new DevnetEntrypoint(); - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + // create the controller + const controller = entrypoint.createSmartContractController(abi); - // create the controller - const controller = entrypoint.createSmartContractController(abi); + // creates the query, runs the query, parses the result + const response = await controller.query({ contract: contractAddress, function: "getSum", arguments: [] }); + } + // ``` - // creates the query, runs the query, parses the result - const response = await controller.query({ contract: contractAddress, function: "getSum", arguments: [] }); -} -``` + // If we need more granular control, we can split the process into three steps: **create the query, run the query, and parse the query response**. + // This approach achieves the same result as the previous example. -If we need more granular control, we can split the process into three steps: **create the query, run the query, and parse the query response**. -This approach achieves the same result as the previous example. + // ```js + { + const entrypoint = new DevnetEntrypoint(); -```js -{ - const entrypoint = new DevnetEntrypoint(); + // load the abi + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); - // load the abi - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); + // the contract address we'll query + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - // the contract address we'll query - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + // create the controller + const controller = entrypoint.createSmartContractController(abi); - // create the controller - const controller = entrypoint.createSmartContractController(abi); + // create the query + const query = await controller.createQuery({ contract: contractAddress, function: "getSum", arguments: [] }); + // runs the query + const response = await controller.runQuery(query); - // create the query - const query = await controller.createQuery({ contract: contractAddress, function: "getSum", arguments: [] }); - // runs the query - const response = await controller.runQuery(query); + // parse the result + const parsedResponse = controller.parseQueryResponse(response); + } + // ``` - // parse the result - const parsedResponse = controller.parseQueryResponse(response); -} -``` + // ### Upgrading a smart contract + // Contract upgrade transactions are similar to deployment transactions (see above) because they also require contract bytecode. + // However, in this case, the contract address is already known. Like deploying a smart contract, we can upgrade a smart contract using either the **controller** or the **factory**. -### Upgrading a smart contract -Contract upgrade transactions are similar to deployment transactions (see above) because they also require contract bytecode. -However, in this case, the contract address is already known. Like deploying a smart contract, we can upgrade a smart contract using either the **controller** or the **factory**. - -#### Uprgrading a smart contract using the controller -```js -{ - // prepare the account - const entrypoint = new DevnetEntrypoint(); - const keystorePath = path.join("src", "testdata", "testwallets", "alice.json"); - const account = Account.newFromKeystore({ - filePath: keystorePath, - password: "password" - }); - // the developer is responsible for managing the nonce - account.nonce = entrypoint.recall_account_nonce(account.address); - - // load the abi - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); - - // create the controller - const controller = entrypoint.createSmartContractController(abi); - - // load the contract bytecode; this is the new contract code, the one we want to upgrade to - const bytecode = await promises.readFile("../contracts/adder.wasm"); - - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: - let args = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: - args = [42]; - - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - - const upgradeTransaction = await controller.createTransactionForUpgrade( - sender, - sender.getNonceThenIncrement(), - { - contract: contractAddress, - bytecode: bytecode, - gasLimit: 6000000n, - arguments: args, - }, - ); - - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(upgradeTransaction); - - console.log({ txHash }); -} -``` + // #### Uprgrading a smart contract using the controller + // ```js + { + // prepare the account + const entrypoint = new DevnetEntrypoint(); + const keystorePath = path.join("../src", "testdata", "testwallets", "alice.json"); + const sender = Account.newFromKeystore(keystorePath, "password"); + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); + + // load the abi + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + + // create the controller + const controller = entrypoint.createSmartContractController(abi); + + // load the contract bytecode; this is the new contract code, the one we want to upgrade to + const bytecode = await promises.readFile("../src/testData/adder.wasm"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + const upgradeTransaction = await controller.createTransactionForUpgrade( + sender, + sender.getNonceThenIncrement(), + { + contract: contractAddress, + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }, + ); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(upgradeTransaction); + + console.log({ txHash }); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); -### Token management +(async () => { + // ### Token management -In this section, we're going to create transactions to issue fungible tokens, issue semi-fungible tokens, create NFTs, set token roles, but also parse these transactions to extract their outcome (e.g. get the token identifier of the newly issued token). + // In this section, we're going to create transactions to issue fungible tokens, issue semi-fungible tokens, create NFTs, set token roles, but also parse these transactions to extract their outcome (e.g. get the token identifier of the newly issued token). -These methods are available through the `TokenManagementController` and the `TokenManagementTransactionsFactory`. -The controller also includes built-in methods for awaiting the completion of transactions and parsing their outcomes. -For the factory, the same functionality can be achieved using the `TokenManagementTransactionsOutcomeParser`. + // These methods are available through the `TokenManagementController` and the `TokenManagementTransactionsFactory`. + // The controller also includes built-in methods for awaiting the completion of transactions and parsing their outcomes. + // For the factory, the same functionality can be achieved using the `TokenManagementTransactionsOutcomeParser`. -For scripts or quick network interactions, we recommend using the controller. However, for a more granular approach (e.g., DApps), the factory is the better choice. + // For scripts or quick network interactions, we recommend using the controller. However, for a more granular approach (e.g., DApps), the factory is the better choice. -#### Issuing fungible tokens using the controller -```js -{ - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createTokenManagementController(); + // #### Issuing fungible tokens using the controller + // ```js + { + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingFungible(alice, alice.getNonceThenIncrement(), { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + const outcome = await controller.awaitCompletedIssueFungible(txHash); + + const tokenIdentifier = outcome[0].tokenIdentifier; + } + // ``` - // create the issuer of the token - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // #### Issuing fungible tokens using the factory + // ```js + { + // create the entrypoint and the token management transactions factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForIssuingFungible(alice.address, { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueFungible(transactionOnNetwork); + const tokenIdentifier = outcome[0].tokenIdentifier; + } + // ``` - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // #### Setting special roles for fungible tokens using the controller + // ```js + { + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await controller.createTransactionForSettingSpecialRoleOnFungibleToken( + alice, + alice.getNonceThenIncrement(), + { + user: bob, + tokenIdentifier: "TEST-123456", + addRoleLocalMint: true, + addRoleLocalBurn: true, + addRoleESDTTransferRole: true, + }, + ); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + const outcome = await controller.awaitCompletedSetSpecialRoleOnFungibleToken(txHash); + + const roles = outcome[0].roles; + const user = outcome[0].userAddress; + } + // ``` - const transaction = await controller.createTransactionForIssuingFungible( - alice, - alice.getNonceThenIncrement(), + // #### Setting special roles for fungible tokens using the factory + // ```js { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - }, - ); + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForIssuingFungible(alice.address, { + tokenName: "TEST", + tokenTicker: "TEST", + initialSupply: 100n, + numDecimals: 0n, + canFreeze: true, + canWipe: true, + canPause: true, + canChangeOwner: true, + canUpgrade: false, + canAddSpecialRoles: false, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseSetSpecialRole(transactionOnNetwork); + + const roles = outcome[0].roles; + const user = outcome[0].userAddress; + } + // ``` - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); + // #### Issuing semi-fungible tokens using the controller + // ```js + { + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingSemiFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWSEMI", + tokenTicker: "SEMI", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }, + ); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + const outcome = await controller.awaitCompletedIssueSemiFungible(txHash); + + const tokenIdentifier = outcome[0].tokenIdentifier; + } + // ``` - // wait for transaction to execute, extract the token identifier - const outcome = await entrypoint.awaitCompletedIssueFungible(txHash); + // #### Issuing semi-fungible tokens using the factory + // ```js + { + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForIssuingSemiFungible(alice.address, { + tokenName: "NEWSEMI", + tokenTicker: "SEMI", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueSemiFungible(transactionOnNetwork); + + const tokenIdentifier = outcome[0].tokenIdentifier; + } + // ``` - const tokenIdentifier = outcome[0].tokenIdentifier; + // #### Issuing NFT collection & creating NFTs using the controller -} -``` + // ```js + { + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + let transaction = await controller.createTransactionForIssuingNonFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWNFT", + tokenTicker: "NFT", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }, + ); + + // sending the transaction + let txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + let outcome = await controller.awaitCompletedIssueNonFungible(txHash); + + const collectionIdentifier = outcome[0].tokenIdentifier; + + // create an NFT + transaction = await controller.createTransactionForCreatingNft(alice, alice.getNonceThenIncrement(), { + tokenIdentifier: "FRANK-aa9e8d", + initialQuantity: 1n, + name: "test", + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + }); + + // sending the transaction + txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + outcome = await controller.awaitCompletedCreateNft(txHash); + + const identifier = outcome[0].tokenIdentifier; + } + // ``` -#### Issuing fungible tokens using the factory -```js -{ - // create the entrypoint and the token management transactions factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); - - // create the issuer of the token - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const transaction = await factory.createTransactionForIssuingFungible( - alice, - { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - }, - ); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier - // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` - const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - - // extract the token identifier - const parser = new TokenManagementTransactionsOutcomeParser(); - const outcome = parser.parseIssueFungible(transactionOnNetwork); - const tokenIdentifier = outcome[0].tokenIdentifier; -} -``` + // #### Issuing NFT collection & creating NFTs using the factory + // ```js + { + // create the entrypoint and the token management transdactions factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + let transaction = await factory.createTransactionForIssuingNonFungible(alice.address, { + tokenName: "NEWNFT", + tokenTicker: "NFT", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + let txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + let transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier + let parser = new TokenManagementTransactionsOutcomeParser(); + let outcome = parser.parseIssueNonFungible(transactionOnNetwork); + + const collectionIdentifier = outcome[0].tokenIdentifier; + + transaction = await factory.createTransactionForCreatingNFT(alice.address, { + tokenIdentifier: "FRANK-aa9e8d", + initialQuantity: 1n, + name: "test", + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + }); + + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + txHash = await entrypoint.sendTransaction(transaction); + + // ### wait for transaction to execute, extract the token identifier + transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + outcome = parser.parseIssueNonFungible(transactionOnNetwork); + + const identifier = outcome[0].tokenIdentifier; + } + // ``` + // These are just a few examples of what you can do using the token management controller or factory. For a complete list of supported methods, please refer to the autogenerated documentation: -#### Setting special roles for fungible tokens using the controller -```js -{ - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createTokenManagementController(); + // - [TokenManagementController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementController.html) + // - [TokenManagementTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementTransactionsFactory.html) +})().catch((e) => { + console.log({ e }); +}); - // create the issuer of the token - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); +(async () => { + // ### Account management - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // The account management controller and factory allow us to create transactions for managing accounts, such as: + // - Guarding and unguarding accounts + // - Saving key-value pairs in the account storage, on the blockchain. - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + // To learn more about Guardians, please refer to the [official documentation](/developers/built-in-functions/#setguardian). + // A guardian can also be set using the WebWallet, which leverages our hosted `Trusted Co-Signer Service`. Follow [this guide](/wallet/web-wallet/#guardian) for step-by-step instructions on guarding an account using the wallet. - const transaction = await controller.createTransactionForSettingSpecialRoleOnFungibleToken( - alice, - alice.getNonceThenIncrement(), + // #### Guarding an account using the controller + // ```js { - user: bob, - tokenIdentifier: "TEST-123456", - addRoleLocalMint: true, - addRoleLocalBurn: true, - addRoleESDTTransferRole: true, - }, - ); - - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); + // create the entrypoint and the account controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); - // wait for transaction to execute, extract the token identifier - const outcome = await entrypoint.awaitCompletedSetSpecialRoleOnFungibleToken(transaction); + // create the account to guard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const roles = outcome[0].roles; - const user = outcome[0].userAddress; -} -``` - -#### Setting special roles for fungible tokens using the factory -```js -{ - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); - - // create the issuer of the token - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - const transaction = await factory.createTransactionForIssuingFungible( - alice, - { - user: bob, - tokenIdentifier: "TEST-123456", - addRoleLocalMint: true, - addRoleLocalBurn: true, - addRoleESDTTransferRole: true, - }, - ); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier - // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` - const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - - const parser = new TokenManagementTransactionsOutcomeParser(); - const outcome = parser.parseSetSpecialRole(transactionOnNetwork); - - const roles = outcome[0].roles; - const user = outcome[0].userAddress; -} -``` + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -#### Issuing semi-fungible tokens using the controller -```js -{ - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createTokenManagementController(); - - // create the issuer of the token - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = await controller.createTransactionForIssuingSemiFungible( - alice, - alice.getNonceThenIncrement(), - { - tokenName: "NEWSEMI", - tokenTicker: "SEMI", - canFreeze: false, - canWipe: true, - canPause: false, - canTransferNFTCreateRole: true, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: true, - }, - ); - - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier - const outcome = await entrypoint.awaitCompletedIssueSemiFungible(txHash); - - const tokenIdentifier = outcome[0].tokenIdentifier; -} -``` + // we can use a trusted service that provides a guardian, or simply set another address we own or trust + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); -#### Issuing semi-fungible tokens using the factory -```js -{ - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); - - // create the issuer of the token - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const transaction = await factory.createTransactionForIssuingSemiFungible( - alice, - { - tokenName: "NEWSEMI", - tokenTicker: "SEMI", - canFreeze: false, - canWipe: true, - canPause: false, - canTransferNFTCreateRole: true, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: true, - }, - ); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier - const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - - // extract the token identifier - const parser = new TokenManagementTransactionsOutcomeParser(); - const outcome = parser.parseIssueSemiFungible(transactionOnNetwork); - - const tokenIdentifier = outcome[0].tokenIdentifier; -} -``` + const transaction = await controller.createTransactionForSettingGuardian(alice, alice.getNonceThenIncrement(), { + guardianAddress: guardian, + serviceID: "SelfOwnedAddress", // this is just an example + }); -#### Issuing NFT collection & creating NFTs using the controller - -```js -{ - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.creatTokenManagementController(); - - // create the issuer of the token - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - let transaction = await controller.createTransactionForIssuingNonFungible( - alice, - alice.getNonceThenIncrement(), - { - tokenName: "NEWNFT", - tokenTicker: "NFT", - canFreeze: false, - canWipe: true, - canPause: false, - canTransferNFTCreateRole: true, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: true, - }, - ); - - // sending the transaction - let txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier - let outcome = await entrypoint.awaitCompletedIssueNonFungible(txHash); - - const collectionIdentifier = outcome[0].tokenIdentifier; - - // create an NFT - transaction = controller.createTransactionForCreatingNft(alice, - alice.getNonceThenIncrement(), - { - tokenIdentifier: "FRANK-aa9e8d", - initialQuantity: 1n, - name: "test", - royalties: 1000, - hash: "abba", - attributes: Buffer.from("test"), - uris: ["a", "b"], - }, - ); - - // sending the transaction - txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier - outcome = await entrypoint.awaitCompletedCreateNft(txHash); - - const identifier = outcome[0].tokenIdentifier; - const nonce = outcome[0].nonce; - const initialQuantity = outcome[0].initialQuantity; - -} -``` - -#### Issuing NFT collection & creating NFTs using the factory -```js -{ - // create the entrypoint and the token management transdactions factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); - - // create the issuer of the token - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - let transaction = await factory.createTransactionForIssuingNonFungible( - alice, - { - tokenName: "NEWNFT", - tokenTicker: "NFT", - canFreeze: false, - canWipe: true, - canPause: false, - canTransferNFTCreateRole: true, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: true, - }, - ); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction - let txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier - let transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - - // extract the token identifier - let parser = new TokenManagementTransactionsOutcomeParser(); - let outcome = parser.parseIssueNonFungible(transactionOnNetwork); - - const collectionIdentifier = outcome[0].tokenIdentifier; - - transaction = await factory.createTransactionForCreatingNFT( - alice, - { - tokenIdentifier: "FRANK-aa9e8d", - initialQuantity: 1n, - name: "test", - royalties: 1000, - hash: "abba", - attributes: Buffer.from("test"), - uris: ["a", "b"], - }, - ); - - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction - txHash = await entrypoint.sendTransaction(transaction); - - // ### wait for transaction to execute, extract the token identifier - transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - - outcome = parser.parseIssueNonFungible(transactionOnNetwork); - - const identifier = outcome[0].tokenIdentifier; - const nonce = outcome[0].nonce; - const initialQuantity = outcome[0].initialQuantity; -} -``` - -These are just a few examples of what you can do using the token management controller or factory. For a complete list of supported methods, please refer to the autogenerated documentation: + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` -- [TokenManagementController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementController.html) -- [TokenManagementTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementTransactionsFactory.html) + // #### Guarding an account using the factory + // ```js + { + // create the entrypoint and the account management factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); -### Account management + // create the account to guard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); -The account management controller and factory allow us to create transactions for managing accounts, such as: -- Guarding and unguarding accounts -- Saving key-value pairs in the account storage, on the blockchain. + // we can use a trusted service that provides a guardian, or simply set another address we own or trust + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); -To learn more about Guardians, please refer to the [official documentation](/developers/built-in-functions/#setguardian). -A guardian can also be set using the WebWallet, which leverages our hosted `Trusted Co-Signer Service`. Follow [this guide](/wallet/web-wallet/#guardian) for step-by-step instructions on guarding an account using the wallet. + const transaction = await factory.createTransactionForSettingGuardian(alice.address, { + guardianAddress: guardian, + serviceID: "SelfOwnedAddress", // this is just an example + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -#### Guarding an account using the controller -```js -{ - // create the entrypoint and the account controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createAccountController(); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - // create the account to guard - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - // we can use a trusted service that provides a guardian, or simply set another address we own or trust - const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + // Once a guardian is set, we must wait **20 epochs** before it can be activated. After activation, all transactions sent from the account must also be signed by the guardian. - const transaction = await controller.createTransactionForSettingGuardian( - alice, - alice.getNonceThenIncrement(), + // #### Activating the guardian using the controller + // ```js { - guardianAddress: guardian, - serviceID: "SelfOwnedAddress", // this is just an example - }, - ); + // create the entrypoint and the account controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // create the account to guard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); -#### Guarding an account using the factory -```js -{ - // create the entrypoint and the account management factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createAccountTransactionsFactory(); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // create the account to guard - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const transaction = await controller.createTransactionForGuardingAccount( + alice, + alice.getNonceThenIncrement(), + {}, + ); - // we can use a trusted service that provides a guardian, or simply set another address we own or trust - const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - const transaction = await factory.createTransactionForSettingGuardian( - alice, + // #### Activating the guardian using the factory + // ```js { - guardianAddress: guardian, - serviceID: "SelfOwnedAddress", // this is just an example - }, - ); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // create the entrypoint and the account factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); -Once a guardian is set, we must wait **20 epochs** before it can be activated. After activation, all transactions sent from the account must also be signed by the guardian. + // create the account to guard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); -#### Activating the guardian using the controller -```js -{ - // create the entrypoint and the account controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createAccountController(); + const transaction = await factory.createTransactionForGuardingAccount(alice.address); - // create the account to guard - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - const transaction = await controller.createTransactionForGuardingAccount( - alice, - alice.getNonceThenIncrement(), - ); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` - -#### Activating the guardian using the factory -```js -{ - // create the entrypoint and the account factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createAccountTransactionsFactory(); - - // create the account to guard - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const transaction = await factory.createTransactionForGuardingAccount( - alice.address, - ); - - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` -#### Unguarding the account using the controller -```js -{ - // create the entrypoint and the account controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createAccountController(); + // #### Unguarding the account using the controller + // ```js + { + // create the entrypoint and the account controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); - // create the account to unguard - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // create the account to unguard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const transaction = await controller.createTransactionForUnguardingAccount( + alice, + alice.getNonceThenIncrement(), + {}, + ); - const transaction = await controller.createTransactionForUnguardingAccount( - alice, - alice.getNonceThenIncrement(), - { - guardian: guardian + // the transaction should also be signed by the guardian before being sent otherwise it won't be executed + const txHash = await entrypoint.sendTransaction(transaction); } - ); + // ``` - // the transaction should also be signed by the guardian before being sent otherwise it won't be executed - const txHash = await entrypoint.sendTransaction(transaction); -} -``` - -#### Unguarding the guardian using the factory -```js -{ - // create the entrypoint and the account factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createAccountTransactionsFactory(); + // #### Unguarding the guardian using the factory + // ```js + { + // create the entrypoint and the account factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); - // create the account to guard - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + // create the account to guard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const transaction = await factory.createTransactionForUnguardingAccount( - alice.address, - guardian - ); + const transaction = await factory.createTransactionForUnguardingAccount(alice.address); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` -#### Saving a key-value pair to an account using the controller -You can find more information [here](/developers/account-storage) regarding the account storage. + // #### Saving a key-value pair to an account using the controller + // You can find more information [here](/developers/account-storage) regarding the account storage. -```js -{ - // create the entrypoint and the account controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createAccountController(); + // ```js + { + // create the entrypoint and the account controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // creating the key-value pairs we want to save - const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); + // creating the key-value pairs we want to save + const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const transaction = await controller.createTransactionForSavingKeyValue(alice, alice.getNonceThenIncrement(), { + keyValuePairs: keyValuePairs, + }); - const transaction = await controller.createTransactionForSavingKeyValue( - alice, - alice.getNonceThenIncrement(), - { - keyValuePairs: keyValuePairs + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); } - ); - - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` - -#### Saving a key-value pair to an account using the factory -```js -{ - // create the entrypoint and the account factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createAccountTransactionsFactory(); - - // create the account to guard - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // creating the key-value pairs we want to save - const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); + // ``` - const transaction = await factory.createTransactionForSavingKeyValue( - alice.address, { - keyValuePairs: keyValuePairs, - }); + // #### Saving a key-value pair to an account using the factory + // ```js + { + // create the entrypoint and the account factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // create the account to guard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + // creating the key-value pairs we want to save + const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + const transaction = await factory.createTransactionForSavingKeyValue(alice.address, { + keyValuePairs: keyValuePairs, + }); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -### Delegation management + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); -To learn more about staking providers and delegation, please refer to the official [documentation](/validators/delegation-manager/#introducing-staking-providers). -In this section, we'll cover how to: -- Create a new delegation contract -- Retrieve the contract address -- Delegate funds to the contract -- Redelegate rewards -- Claim rewards -- Undelegate and withdraw funds + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); -These operations can be performed using both the controller and the **factory**. For a complete list of supported methods, please refer to the autogenerated documentation: -- [DelegationController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationController.html) -- [DelegationTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationTransactionsFactory.html) + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); -#### Creating a New Delegation Contract Using the Controller -```js -{ - // create the entrypoint and the delegation controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); +(async () => { + // ### Delegation management - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // To learn more about staking providers and delegation, please refer to the official [documentation](/validators/delegation-manager/#introducing-staking-providers). + // In this section, we'll cover how to: + // - Create a new delegation contract + // - Retrieve the contract address + // - Delegate funds to the contract + // - Redelegate rewards + // - Claim rewards + // - Undelegate and withdraw funds - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // These operations can be performed using both the controller and the **factory**. For a complete list of supported methods, please refer to the autogenerated documentation: + // - [DelegationController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationController.html) + // - [DelegationTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationTransactionsFactory.html) - const transaction = await controller.createTransactionForNewDelegationContract( - alice.address, - alice.getNonceThenIncrement(), + // #### Creating a New Delegation Contract Using the Controller + // ```js { - totalDelegationCap: 0, - serviceFee: 10n, - amount: 1250000000000000000000n, - }); + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // wait for transaction completion, extract delegation contract's address - const outcome = await controller.awaitCompletedCreateNewDelegationContract(txHash); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const contractAddress = outcome[0].contractAddress; -} -``` + const transaction = await controller.createTransactionForNewDelegationContract( + alice, + alice.getNonceThenIncrement(), + { + totalDelegationCap: 0n, + serviceFee: 10n, + amount: 1250000000000000000000n, + }, + ); -#### Creating a new delegation contract using the factory -```js -{ - // create the entrypoint and the delegation factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // wait for transaction completion, extract delegation contract's address + const outcome = await controller.awaitCompletedCreateNewDelegationContract(txHash); + const contractAddress = outcome[0].contractAddress; + } + // ``` - const transaction = await factory.createTransactionForNewDelegationContract(alice.address, + // #### Creating a new delegation contract using the factory + // ```js { - totalDelegationCap: 0, - serviceFee: 10n, - amount: 1250000000000000000000n, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + const transaction = await factory.createTransactionForNewDelegationContract(alice.address, { + totalDelegationCap: 0n, + serviceFee: 10n, + amount: 1250000000000000000000n, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - // waits until the transaction is processed and fetches it from the network - const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // extract the contract address - const parser = new TokenManagementTransactionsOutcomeParser(); - const outcome = parser.parseIssueFungible(transactionOnNetwork); - const contractAddress = outcome[0].contractAddress; -} -``` - -#### Delegating funds to the contract using the Controller -We can send funds to a delegation contract to earn rewards. - -```js -{ - // create the entrypoint and the delegation controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // waits until the transaction is processed and fetches it from the network + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // extract the token identifier + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueFungible(transactionOnNetwork); + const tokenIdentifier = outcome[0].tokenIdentifier; + } + // ``` - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // #### Delegating funds to the contract using the Controller + // We can send funds to a delegation contract to earn rewards. - const transaction = await controller.createTransactionForDelegating( - alice.address, - alice.getNonceThenIncrement(), + // ```js { - delegationContract: contract, - amount: 5000000000000000000000n, - }); + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -#### Delegating funds to the contract using the factory -```js -{ - // create the entrypoint and the delegation factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const transaction = await controller.createTransactionForDelegating(alice, alice.getNonceThenIncrement(), { + delegationContract: contract, + amount: 5000000000000000000000n, + }); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - const transaction = await factory.createTransactionForDelegating(alice.address, + // #### Delegating funds to the contract using the factory + // ```js { - delegationContract: contract, - amount: 5000000000000000000000n, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + const transaction = await factory.createTransactionForDelegating(alice.address, { + delegationContract: contract, + amount: 5000000000000000000000n, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -#### Redelegating rewards using the Controller -Over time, as rewards accumulate, we may choose to redelegate them to the contract to maximize earnings. + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); -```js -{ - // create the entrypoint and the delegation controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // #### Redelegating rewards using the Controller + // Over time, as rewards accumulate, we may choose to redelegate them to the contract to maximize earnings. - const transaction = await controller.createTransactionForRedelegatingRewards( - alice.address, - alice.getNonceThenIncrement(), + // ```js { - delegationContract: contract, - }); - - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` - -#### Redelegating rewards using the factory -```js -{ - // create the entrypoint and the delegation factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForRedelegatingRewards( + alice, + alice.getNonceThenIncrement(), + { + delegationContract: contract, + }, + ); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - const transaction = await factory.createTransactionForRedelegatingRewards(alice.address, + // #### Redelegating rewards using the factory + // ```js { - delegationContract: contract, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + const transaction = await factory.createTransactionForRedelegatingRewards(alice.address, { + delegationContract: contract, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -#### Claiming rewards using the Controller -We can also claim our rewards when needed. + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); -```js -{ - // create the entrypoint and the delegation controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // #### Claiming rewards using the Controller + // We can also claim our rewards when needed. - const transaction = await controller.createTransactionForClaimingRewards( - alice.address, - alice.getNonceThenIncrement(), + // ```js { - delegationContract: contract, - }); + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); -#### Claiming rewards using the factory -```js -{ - // create the entrypoint and the delegation factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const transaction = await controller.createTransactionForClaimingRewards(alice, alice.getNonceThenIncrement(), { + delegationContract: contract, + }); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - const transaction = await factory.createTransactionForClaimingRewards(alice.address, + // #### Claiming rewards using the factory + // ```js { - delegationContract: contract, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + const transaction = await factory.createTransactionForClaimingRewards(alice.address, { + delegationContract: contract, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -#### Undelegating funds using the Controller -By **undelegating**, we signal the contract that we want to retrieve our staked funds. This process requires a **10-epoch unbonding period** before the funds become available. + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); -```js -{ - // create the entrypoint and the delegation controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // #### Undelegating funds using the Controller + // By **undelegating**, we signal the contract that we want to retrieve our staked funds. This process requires a **10-epoch unbonding period** before the funds become available. - const transaction = await controller.createTransactionForUndelegating( - alice.address, - alice.getNonceThenIncrement(), + // ```js { - delegationContract: contract, - amount: 1000000000000000000000n // 1000 EGLD - }); + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); -#### Undelegating funds using the factory -```js -{ - // create the entrypoint and the delegation factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const transaction = await controller.createTransactionForUndelegating(alice, alice.getNonceThenIncrement(), { + delegationContract: contract, + amount: 1000000000000000000000n, // 1000 EGLD + }); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - const transaction = await factory.createTransactionForUndelegating(alice.address, + // #### Undelegating funds using the factory + // ```js { - delegationContract: contract, - amount: 1000000000000000000000n // 1000 EGLD - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); -#### Withdrawing funds using the Controller -After the `10-epoch unbonding period` is complete, we can proceed with withdrawing our staked funds using the controller. This final step allows us to regain access to the previously delegated funds. + const transaction = await factory.createTransactionForUndelegating(alice.address, { + delegationContract: contract, + amount: 1000000000000000000000n, // 1000 EGLD + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -```js -{ - // create the entrypoint and the delegation controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // #### Withdrawing funds using the Controller + // After the `10-epoch unbonding period` is complete, we can proceed with withdrawing our staked funds using the controller. This final step allows us to regain access to the previously delegated funds. - const transaction = await controller.createTransactionForWithdrawing( - alice.address, - alice.getNonceThenIncrement(), + // ```js { - delegationContract: contract, - }); + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); -#### Withdrawing funds using the factory -```js -{ - // create the entrypoint and the delegation factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + const transaction = await controller.createTransactionForWithdrawing(alice, alice.getNonceThenIncrement(), { + delegationContract: contract, + }); - const transaction = await factory.createTransactionForWithdrawing(alice.address, + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Withdrawing funds using the factory + // ```js { - delegationContract: contract, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + const transaction = await factory.createTransactionForWithdrawing(alice.address, { + delegationContract: contract, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -### Relayed transactions -We are currently on the `third iteration (V3)` of relayed transactions. V1 and V2 will soon be deactivated, so we will focus on V3. - -For V3, two new fields have been added to transactions: -- relayer -- relayerSignature - -Signing Process: -1. The relayer must be set before the sender signs the transaction. -2. Once the sender has signed, the relayer can also sign the transaction and broadcast it. - -**Important Consideration**: -Relayed V3 transactions require an additional `50,000` gas. -Let’s see how to create a relayed transaction: - -```js -{ - const walletsPath = path.join("src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); - const bob = await Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); - - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = new Transaction({ - chainID: "D", - sender: alice.address, - receiver: bob, - relayer: carol.address, - gasLimit: 110_000n, - data: Buffer.from("hello"), - nonce: alice.getNonceThenIncrement() - }); - - // sender signs the transaction - transaction.signature = await alice.signTransaction(transaction); - - // relayer signs the transaction - transaction.RelayerSignature = await carol.signTransaction(transaction); - - // broadcast the transaction - const entrypoint = new DevnetEntrypoint(); - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); -#### Creating relayed transactions using controllers -We can create relayed transactions using any of the available controllers. -Each controller includes a relayer argument, which must be set if we want to create a relayed transaction. - -Let’s issue a fungible token using a relayed transaction: - -```js -{ - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.creatTokenManagementController(); - - // create the issuer of the token - const walletsPath = path.join("src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); - - // Carol will be our relayer, that means she is paying the gas for the transaction - const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); - - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = await controller.createTransactionForIssuingFungible( - alice, - alice.getNonceThenIncrement(), - { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - guardian: carol.address, - }, - ); - - // relayer also signs the transaction - transaction.relayerSignature = await carol.signTransaction(transaction); - - // broadcast the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); -#### Creating relayed transactions using factories -Unlike controllers, `transaction factories` do not have a `relayer` argument. Instead, the **relayer must be set after creating the transaction**. -This approach is beneficial because the **transaction is not signed by the sender at the time of creation**, allowing flexibility in setting the relayer before signing. - -Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: - -```js -{ - // create the entrypoint and the token management factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.creatTokenManagementController(); - - // create the issuer of the token - const walletsPath = path.join("src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); - - // carol will be our relayer, that means she is paying the gas for the transaction - const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); - - const transaction = await factory.createTransactionForIssuingFungible( - alice.address, - { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - }, - ); - - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // set the relayer - transaction.relayer = carol.address; - - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); - - // relayer also signs the transaction - transaction.relayerSignature = await carol.signTransaction(transaction); - - // broadcast the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); -### Guarded transactions -Similar to relayers, transactions also have two additional fields: +(async () => { + // ### Relayed transactions + // We are currently on the `third iteration (V3)` of relayed transactions. V1 and V2 will soon be deactivated, so we will focus on V3. -- guardian -- guardianSignature + // For V3, two new fields have been added to transactions: + // - relayer + // - relayerSignature -Each controller includes an argument for the guardian. The transaction can either: -1. Be sent to a service that signs it using the guardian’s account, or -2. Be signed by another account acting as a guardian. + // Signing Process: + // 1. The relayer must be set before the sender signs the transaction. + // 2. Once the sender has signed, the relayer can also sign the transaction and broadcast it. -Let’s issue a token using a guarded account: + // **Important Consideration**: + // Relayed V3 transactions require an additional `50,000` gas. + // Let’s see how to create a relayed transaction: -#### Creating guarded transactions using controllers -We can create guarded transactions using any of the available controllers. + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const walletsPath = path.join("../src", "testdata", "testwallets"); + const bob = await Account.newFromPem(path.join(walletsPath, "bob.pem")); + const grace = Address.newFromBech32("erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede"); + const mike = await Account.newFromPem(path.join(walletsPath, "mike.pem")); + + // fetch the nonce of the network + bob.nonce = await entrypoint.recallAccountNonce(bob.address); + + const transaction = new Transaction({ + chainID: "D", + sender: bob.address, + receiver: grace, + relayer: mike.address, + gasLimit: 110_000n, + data: Buffer.from("hello"), + nonce: bob.getNonceThenIncrement(), + }); + + // sender signs the transaction + transaction.signature = await bob.signTransaction(transaction); + + // relayer signs the transaction + transaction.relayerSignature = await mike.signTransaction(transaction); + + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` -Each controller method includes a guardian argument, which must be set if we want to create a guarded transaction. -Let’s issue a fungible token using a relayed transaction: + // #### Creating relayed transactions using controllers + // We can create relayed transactions using any of the available controllers. + // Each controller includes a relayer argument, which must be set if we want to create a relayed transaction. -```js -{ - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.creatTokenManagementController(); + // Let’s issue a fungible token using a relayed transaction: - // create the issuer of the token - const walletsPath = path.join("src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + // ```js + { + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token + const walletsPath = path.join("../src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // Carol will be our relayer, that means she is paying the gas for the transaction + const frank = await Account.newFromPem(path.join(walletsPath, "frank.pem")); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingFungible(alice, alice.getNonceThenIncrement(), { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + relayer: frank.address, + }); + + // relayer also signs the transaction + transaction.relayerSignature = await frank.signTransaction(transaction); + + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - // carol will be our guardian - const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + // #### Creating relayed transactions using factories + // Unlike controllers, `transaction factories` do not have a `relayer` argument. Instead, the **relayer must be set after creating the transaction**. + // This approach is beneficial because the **transaction is not signed by the sender at the time of creation**, allowing flexibility in setting the relayer before signing. - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: - const transaction = await controller.createTransactionForIssuingFungible( - alice, - alice.getNonceThenIncrement(), + // ```js { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - guardian: carol.address, - }, - ); + // create the entrypoint and the token management factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token + const walletsPath = path.join("../src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // carol will be our relayer, that means she is paying the gas for the transaction + const frank = await Account.newFromPem(path.join(walletsPath, "frank.pem")); + + const transaction = await factory.createTransactionForIssuingFungible(alice.address, { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // set the relayer + transaction.relayer = frank.address; + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // relayer also signs the transaction + transaction.relayerSignature = await frank.signTransaction(transaction); + + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); - // guardian also signs the transaction - transaction.guardianSignature = await carol.signTransaction(transaction); +(async () => { + // ### Guarded transactions + // Similar to relayers, transactions also have two additional fields: - // broadcast the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // - guardian + // - guardianSignature -#### Creating guarded transactions using factories -Unlike controllers, `transaction factories` do not have a `guardian` argument. Instead, the **guardian must be set after creating the transaction**. -This approach is beneficial because the transaction is **not signed by the sender at the time of creation**, allowing flexibility in setting the guardian before signing. - -Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: - -```js -{ - // create the entrypoint and the token management factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementController(); - - // create the issuer of the token - const walletsPath = path.join("src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); - - // carol will be our guardian - const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); - - const transaction = await factory.createTransactionForIssuingFungible( - alice.address, - { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - }, - ); - - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // set the guardian - transaction.guardian = carol.address; - - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); - - // guardian also signs the transaction - transaction.guardianSignature = await carol.signTransaction(transaction); - - // broadcast the transaction - const txHash = await entrypoint.sendTransaction(transaction); -} -``` + // Each controller includes an argument for the guardian. The transaction can either: + // 1. Be sent to a service that signs it using the guardian’s account, or + // 2. Be signed by another account acting as a guardian. -We can create guarded relayed transactions just like we did before. However, keep in mind: + // Let’s issue a token using a guarded account: -Only the sender can be guarded, the relayer cannot be guarded. + // #### Creating guarded transactions using controllers + // We can create guarded transactions using any of the available controllers. -Flow for Creating Guarded Relayed Transactions: -- Using Controllers: -1. Set both guardian and relayer fields. -2. The transaction must be signed by both the guardian and the relayer. -- Using Factories: + // Each controller method includes a guardian argument, which must be set if we want to create a guarded transaction. + // Let’s issue a fungible token using a relayed transaction: -1. Create the transaction. -2. Set both guardian and relayer fields. -3. First, the sender signs the transaction. -4. Then, the guardian signs. -5. Finally, the relayer signs before broadcasting. + // ```js + { + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token + const walletsPath = path.join("../src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // carol will be our guardian + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingFungible(alice, alice.getNonceThenIncrement(), { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + guardian: carol.address, + }); + + // guardian also signs the transaction + transaction.guardianSignature = await carol.signTransaction(transaction); + + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` -## Addresses + // #### Creating guarded transactions using factories + // Unlike controllers, `transaction factories` do not have a `guardian` argument. Instead, the **guardian must be set after creating the transaction**. + // This approach is beneficial because the transaction is **not signed by the sender at the time of creation**, allowing flexibility in setting the guardian before signing. -Create an `Address` object from a bech32-encoded string: + // Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: -``` js -{ - // Create an Address object from a bech32-encoded string - const address = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + // ```js + { + // create the entrypoint and the token management factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token + const walletsPath = path.join("../src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // carol will be our guardian + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + const transaction = await factory.createTransactionForIssuingFungible(alice.address, { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }); + + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // set the guardian + transaction.guardian = carol.address; + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // guardian also signs the transaction + transaction.guardianSignature = await carol.signTransaction(transaction); + + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` - console.log("Address (bech32-encoded):", address.toBech32()); - console.log("Public key (hex-encoded):", address.toHex()); - console.log("Public key (hex-encoded):", Buffer.from(address.getPublicKey()).toString("hex")); -} + // We can create guarded relayed transactions just like we did before. However, keep in mind: -``` + // Only the sender can be guarded, the relayer cannot be guarded. -Here’s how you can create an address from a hex-encoded string using the MultiversX JavaScript SDK: -If the HRP (human-readable part) is not provided, the SDK will use the default one ("erd"). + // Flow for Creating Guarded Relayed Transactions: + // - Using Controllers: + // 1. Set both guardian and relayer fields. + // 2. The transaction must be signed by both the guardian and the relayer. + // - Using Factories: -``` js -{ - // Create an address from a hex-encoded string with a specified HRP - const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "erd"); + // 1. Create the transaction. + // 2. Set both guardian and relayer fields. + // 3. First, the sender signs the transaction. + // 4. Then, the guardian signs. + // 5. Finally, the relayer signs before broadcasting. +})().catch((e) => { + console.log({ e }); +}); - console.log("Address (bech32-encoded):", address.toBech32()); - console.log("Public key (hex-encoded):", address.toHex()); -} -``` +(async () => { + // ## Addresses -#### Create an address from a raw public key + // Create an `Address` object from a bech32-encoded string: -``` js -{ - const pubkey = Buffer.from("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "hex"); - const addressFromPubkey = new Address(pubkey, "erd"); -} -``` + // ``` js + { + // Create an Address object from a bech32-encoded string + const address = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); -#### Using an AddressFactory to create addresses -AddressFactory allows creating addresses with a custom HRP, ensuring consistency across your application. + console.log("Address (bech32-encoded):", address.toBech32()); + console.log("Public key (hex-encoded):", address.toHex()); + console.log("Public key (hex-encoded):", Buffer.from(address.getPublicKey()).toString("hex")); + } -``` js -{ - const factory = new AddressFactory("erd"); + // ``` - const address1 = factory.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const address2 = factory.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); - const address3 = factory.fromPublicKey(Buffer.from("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "hex")); -} -``` + // Here’s how you can create an address from a hex-encoded string using the MultiversX JavaScript SDK: + // If the HRP (human-readable part) is not provided, the SDK will use the default one ("erd"). -#### Getting the shard of an address -``` js + // ``` js + { + // Create an address from a hex-encoded string with a specified HRP + const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "erd"); -const addressComputer = new AddressComputer(); -console.log("Shard:", addressComputer.getShardOfAddress(address)); -``` + console.log("Address (bech32-encoded):", address.toBech32()); + console.log("Public key (hex-encoded):", address.toHex()); + } + // ``` -Checking if an address is a smart contract -``` js + // #### Create an address from a raw public key -const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm"); -console.log("Is contract address:", contractAddress.isSmartContract()); -``` + // ``` js + { + const pubkey = Buffer.from("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "hex"); + const addressFromPubkey = new Address(pubkey, "erd"); + } + // ``` -### Changing the default hrp -The **LibraryConfig** class manages the default **HRP** (human-readable part) for addresses, which is set to `"erd"` by default. -You can change the HRP when creating an address or modify it globally in **LibraryConfig**, affecting all newly created addresses. -``` js + // #### Getting the shard of an address + // ``` js -console.log(LibraryConfig.defaultAddressHrp); -const defaultAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); -console.log(defaultAddress.toBech32()); + const addressComputer = new AddressComputer(); + const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); + console.log("Shard:", addressComputer.getShardOfAddress(address)); + // ``` -LibraryConfig.defaultAddressHrp = "test"; -const testAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); -console.log(testAddress.toBech32()); + // Checking if an address is a smart contract + // ``` js -// Reset HRP back to "erd" to avoid affecting other parts of the application. -LibraryConfig.defaultAddressHrp = "erd"; -``` + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm"); + console.log("Is contract address:", contractAddress.isSmartContract()); + // ``` -## Wallets + // ### Changing the default hrp + // The **LibraryConfig** class manages the default **HRP** (human-readable part) for addresses, which is set to `"erd"` by default. + // You can change the HRP when creating an address or modify it globally in **LibraryConfig**, affecting all newly created addresses. + // ``` js -#### Generating a mnemonic -Mnemonic generation is based on [bip39](https://www.npmjs.com/package/bip39) and can be achieved as follows: + console.log(LibraryConfig.DefaultAddressHrp); + const defaultAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); + console.log(defaultAddress.toBech32()); -``` js + LibraryConfig.DefaultAddressHrp = "test"; + const testAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); + console.log(testAddress.toBech32()); -const mnemonic = Mnemonic.generate(); -const words = mnemonic.getWords(); -console.log({ words }); -``` + // Reset HRP back to "erd" to avoid affecting other parts of the application. + LibraryConfig.DefaultAddressHrp = "erd"; + // ``` +})().catch((e) => { + console.log({ e }); +}); + +(async () => { + // ## Wallets -#### Saving the mnemonic to a keystore file -The mnemonic can be saved to a keystore file: + // #### Generating a mnemonic + // Mnemonic generation is based on [bip39](https://www.npmjs.com/package/bip39) and can be achieved as follows: + + // ``` js -``` js -{ const mnemonic = Mnemonic.generate(); + const words = mnemonic.getWords(); + console.log({ words }); + // ``` - // saves the mnemonic to a keystore file with kind=mnemonic - const wallet = UserWallet.fromMnemonic({ mnemonic: mnemonic.getText(), password: "password" }); + // #### Saving the mnemonic to a keystore file + // The mnemonic can be saved to a keystore file: - const filePath = path.join("src", "testdata", "testwallets", "walletWithMnemonic.json"); - wallet.save(filePath); -} -``` + // ``` js + { + const mnemonic = Mnemonic.generate(); -#### Deriving secret keys from a mnemonic -Given a mnemonic, we can derive keypairs: + // saves the mnemonic to a keystore file with kind=mnemonic + const wallet = UserWallet.fromMnemonic({ mnemonic: mnemonic.toString(), password: "password" }); -``` js -{ - const mnemonic = Mnemonic.generate(); + const filePath = path.join("../src", "testdata", "testwallets", "walletWithMnemonic.json"); + console.log({ filePath }); + wallet.save(filePath); + } + // ``` - const secretKey = mnemonic.deriveKey(0); - const publicKey = secretKey.generatePublicKey(); + // #### Deriving secret keys from a mnemonic + // Given a mnemonic, we can derive keypairs: - console.log("Secret key: ", secretKey.hex()); - console.log("Public key: ", publicKey.hex()); -} -``` + // ``` js + { + const mnemonic = Mnemonic.generate(); -#### Saving a secret key to a keystore file -The secret key can also be saved to a keystore file: + const secretKey = mnemonic.deriveKey(0); + const publicKey = secretKey.generatePublicKey(); -``` js -{ - const mnemonic = Mnemonic.generate(); - const secretKey = mnemonic.deriveKey(); + console.log("Secret key: ", secretKey.hex()); + console.log("Public key: ", publicKey.hex()); + } + // ``` - const wallet = UserWallet.fromSecretKey({ secretKey: secretKey, password: "password" }); + // #### Saving a secret key to a keystore file + // The secret key can also be saved to a keystore file: - const filePath = path.join("src", "testdata", "testwallets", "walletWithSecretKey.json"); - wallet.save(filePath); -} -``` + // ``` js + { + const mnemonic = Mnemonic.generate(); + const secretKey = mnemonic.deriveKey(); -#### Saving a secret key to a PEM file -We can save a secret key to a pem file. *This is not recommended as it is not secure, but it's very convenient for testing purposes.* + const wallet = UserWallet.fromSecretKey({ secretKey: secretKey, password: "password" }); -``` js -{ - const mnemonic = Mnemonic.generate(); + const filePath = path.join("../src", "testdata", "testwallets", "walletWithSecretKey.json"); + wallet.save(filePath); + } + // ``` - // by default, derives using the index = 0 - const secretKey = mnemonic.deriveKey(); - const publicKey = secretKey.generatePublicKey(); + // #### Saving a secret key to a PEM file + // We can save a secret key to a pem file. *This is not recommended as it is not secure, but it's very convenient for testing purposes.* - const label = publicKey.toAddress().toBech32(); - const pem = new UserPem(label, secretKey); + // ``` js + { + const mnemonic = Mnemonic.generate(); - const filePath = path.join("src", "testdata", "testwallets", "wallet.pem"); - pem.save(filePath); -} -``` + // by default, derives using the index = 0 + const secretKey = mnemonic.deriveKey(); + const publicKey = secretKey.generatePublicKey(); -#### Generating a KeyPair -A `KeyPair` is a wrapper over a secret key and a public key. We can create a keypair and use it for signing or verifying. + const label = publicKey.toAddress().toBech32(); + const pem = new UserPem(label, secretKey); -``` js -{ - const keypair = KeyPair.generate(); + const filePath = path.join("../src", "testdata", "testwallets", "wallet.pem"); + pem.save(filePath); + } + // ``` - // by default, derives using the index = 0 - const secretKey = keypair.getSecretKey(); - const publicKey = keypair.getPublicKey(); -} -``` + // #### Generating a KeyPair + // A `KeyPair` is a wrapper over a secret key and a public key. We can create a keypair and use it for signing or verifying. -#### Loading a wallet from keystore mnemonic file -Load a keystore that holds an encrypted mnemonic (and perform wallet derivation at the same time): + // ``` js + { + const keypair = KeyPair.generate(); -``` js -{ - const filePath = path.join("src", "testdata", "testwallets", "walletWithMnemonic.json"); + // by default, derives using the index = 0 + const secretKey = keypair.getSecretKey(); + const publicKey = keypair.getPublicKey(); + } + // ``` - // loads the mnemonic and derives the a secret key; default index = 0 - let secretKey = UserWallet.loadSecretKey(filePath, "password"); - let address = secretKey.generatePublicKey().toAddress('erd'); + // #### Loading a wallet from keystore mnemonic file + // Load a keystore that holds an encrypted mnemonic (and perform wallet derivation at the same time): - console.log("Secret key: ", secretKey.hex()); - console.log("Address: ", address.toBech32()); + // ``` js + { + const filePath = path.join("../src", "testdata", "testwallets", "walletWithMnemonic.json"); - // derive secret key with index = 7 - secretKey = UserWallet.loadSecretKey(path, "password", 7); - address = secretKey.generatePublicKey().toAddress(); + // loads the mnemonic and derives the a secret key; default index = 0 + let secretKey = UserWallet.loadSecretKey(filePath, "password"); + let address = secretKey.generatePublicKey().toAddress("erd"); - console.log("Secret key: ", secretKey.hex()); - console.log("Address: ", address.toBech32()); -} -``` + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); -#### Loading a wallet from a keystore secret key file + // derive secret key with index = 7 + secretKey = UserWallet.loadSecretKey(filePath, "password", 7); + address = secretKey.generatePublicKey().toAddress(); -``` js -{ - const filePath = path.join("src", "testdata", "testwallets", "walletWithSecretKey.json"); + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); + } + // ``` - let secretKey = UserWallet.loadSecretKey(filePath, "password"); - let address = secretKey.generatePublicKey().toAddress('erd'); + // #### Loading a wallet from a keystore secret key file - console.log("Secret key: ", secretKey.hex()); - console.log("Address: ", address.toBech32()); -} -``` + // ``` js + { + const filePath = path.join("../src", "testdata", "testwallets", "walletWithSecretKey.json"); -#### Loading a wallet from a PEM file + let secretKey = UserWallet.loadSecretKey(filePath, "password"); + let address = secretKey.generatePublicKey().toAddress("erd"); -``` js -{ - const filePath = path.join("src", "testdata", "testwallets", "wallet.pem"); + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); + } + // ``` - let pem = UserPem.fromFile(filePath); + // #### Loading a wallet from a PEM file - console.log("Secret key: ", pem.secretKey.hex()); - console.log("Public key: ", pem.publicKey.hex()); -} -``` + // ``` js + { + const filePath = path.join("../src", "testdata", "testwallets", "wallet.pem"); -## Signing objects - -Signing is done using an account's secret key. To simplify this process, we provide wrappers like [Account](#creating-accounts), which streamline signing operations. -First, we'll explore how to sign using an Account, followed by signing directly with a secret key. - -#### Signing a Transaction using an Account -We are going to assume we have an account at this point. If you don't, feel free to check out the [creating an account](#creating-accounts) section. -```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const transaction = new Transaction({ - chainID: "D", - sender: alice.address, - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - gasLimit: 50000n, - nonce: 90n - }); - - transaction.signature = await alice.signTransaction(transaction); - console.log(transaction.toPlainObject()); -} -``` + let pem = UserPem.fromFile(filePath); -#### Signing a Transaction using a SecretKey -```js -{ - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = UserSecretKey.fromString(secretKeyHex); - const publickKey = secretKey.generatePublicKey(); - - const transaction = new Transaction({ - nonce: 90n, - sender: publickKey.toAddress(), - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - value: 1000000000000000000n, - gasLimit: 50000n, - chainID: "D" - }); - - // serialize the transaction - const transactionComputer = new TransactionComputer(); - const serializedTransaction = transactionComputer.computeBytesForSigning(transaction); - - // apply the signature on the transaction - transaction.signature = await secretKey.sign(serializedTransaction); - - console.log(transaction.toPlainObject()); -} -``` + console.log("Secret key: ", pem.secretKey.hex()); + console.log("Public key: ", pem.publicKey.hex()); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); -#### Signing a Transaction by hash -```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); +(async () => { + // ## Signing objects - const transaction = new Transaction({ - nonce: 90n, - sender: publickKey.toAddress(), - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - value: 1000000000000000000n, - gasLimit: 50000n, - chainID: "D" - }); + // Signing is done using an account's secret key. To simplify this process, we provide wrappers like [Account](#creating-accounts), which streamline signing operations. + // First, we'll explore how to sign using an Account, followed by signing directly with a secret key. - const transactionComputer = new TransactionComputer(); + // #### Signing a Transaction using an Account + // We are going to assume we have an account at this point. If you don't, feel free to check out the [creating an account](#creating-accounts) section. + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + chainID: "D", + sender: alice.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + gasLimit: 50000n, + nonce: 90n, + }); + + transaction.signature = await alice.signTransaction(transaction); + console.log(transaction.toPlainObject()); + } + // ``` - // sets the least significant bit of the options field to `1` - transactionComputer.applyOptionsForHashSigning(transaction); + // #### Signing a Transaction using a SecretKey + // ```js + { + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = UserSecretKey.fromString(secretKeyHex); + const publickKey = secretKey.generatePublicKey(); + + const transaction = new Transaction({ + nonce: 90n, + sender: publickKey.toAddress(), + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D", + }); + + // serialize the transaction + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForSigning(transaction); + + // apply the signature on the transaction + transaction.signature = await secretKey.sign(serializedTransaction); + + console.log(transaction.toPlainObject()); + } + // ``` - // compute a keccak256 hash for signing - const hash = transactionComputer.computeHashForSigning(transaction); + // #### Signing a Transaction by hash + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // sign and apply the signature on the transaction - transaction.signature = await alice.signTransaction(hash); + const transaction = new Transaction({ + nonce: 90n, + sender: alice.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D", + }); - console.log(transaction.toPlainObject()); -} -``` + const transactionComputer = new TransactionComputer(); -#### Signing a Message using an Account: -```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // sets the least significant bit of the options field to `1` + transactionComputer.applyOptionsForHashSigning(transaction); - const message = new Message({ - data: new Uint8Array(Buffer.from("hello")), - address: alice.address - }); + // compute a keccak256 hash for signing + const hash = transactionComputer.computeHashForSigning(transaction); - message.signature = await alice.signMessage(message); -} -``` + // sign and apply the signature on the transaction + transaction.signature = await alice.signTransaction(transaction); -#### Signing a Message using an SecretKey: -```js -{ - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = UserSecretKey.fromString(secretKeyHex); - const publicKey = secretKey.generatePublicKey(); - - const messageComputer = new MessageComputer(); - const message = new Message({ - data: new Uint8Array(Buffer.from("hello")), - address: publicKey.toAddress() - }); - // serialized the message - const serialized = messageComputer.computeBytesForSigning(message); - - message.signature = await secretKey.sign(serialized); -} -``` + console.log(transaction.toPlainObject()); + } + // ``` -## Verifying signatures + // #### Signing a Message using an Account: + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); -Signature verification is performed using an account’s public key. -To simplify this process, we provide wrappers over public keys that make verification easier and more convenient. + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: alice.address, + }); -#### Verifying Transaction signature using a UserVerifier + message.signature = await alice.signMessage(message); + } + // ``` -```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const account = await Account.newFromPem(filePath); + // #### Signing a Message using an SecretKey: + // ```js + { + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = UserSecretKey.fromString(secretKeyHex); + const publicKey = secretKey.generatePublicKey(); + + const messageComputer = new MessageComputer(); + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: publicKey.toAddress(), + }); + // serialized the message + const serialized = messageComputer.computeBytesForSigning(message); + + message.signature = await secretKey.sign(serialized); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); - const transaction = new Transaction({ - nonce: 90n, - sender: account.address, - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - value: 1000000000000000000n, - gasLimit: 50000n, - chainID: "D" - }); +import { + Account, + Address, + Message, + MessageComputer, + Transaction, + TransactionComputer, + UserPublicKey, + UserVerifier, +(async () => { + // ## Verifying signatures + + // Signature verification is performed using an account’s public key. + // To simplify this process, we provide wrappers over public keys that make verification easier and more convenient. + + // #### Verifying Transaction signature using a UserVerifier + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); - // sign and apply the signature on the transaction - transaction.signature = await account.sign(transaction); + const transaction = new Transaction({ + nonce: 90n, + sender: account.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D", + }); - // instantiating a user verifier; basically gets the public key - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const aliceVerifier = UserVerifier.fromAddress(alice); + // sign and apply the signature on the transaction + transaction.signature = await account.signTransaction(transaction); - // serialize the transaction for verification - const transactionComputer = new TransactionComputer(); - const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); + // instantiating a user verifier; basically gets the public key + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const aliceVerifier = UserVerifier.fromAddress(alice); - // verify the signature - const isSignedByAlice = aliceVerifier.verify(serializedTransaction, transaction.signature); + // serialize the transaction for verification + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); + // verify the signature + const isSignedByAlice = aliceVerifier.verify(serializedTransaction, transaction.signature); - console.log("Transaction is signed by Alice: ", isSignedByAlice); -} -``` + console.log("Transaction is signed by Alice: ", isSignedByAlice); + } + // ``` -#### Verifying Message signature using a UserVerifier + // #### Verifying Message signature using a UserVerifier -```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const account = await Account.newFromPem(filePath); + // ```ts + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); - const message = new Message({ - data: new Uint8Array(Buffer.from("hello")), - address: account.address - }); + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: account.address, + }); - // sign and apply the signature on the message - message.signature = await account.sign(message); + // sign and apply the signature on the message + message.signature = await account.signMessage(message); - // instantiating a user verifier; basically gets the public key - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const aliceVerifier = UserVerifier.fromAddress(alice); + // instantiating a user verifier; basically gets the public key + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const aliceVerifier = UserVerifier.fromAddress(alice); - // serialize the message for verification - const messageComputer = new MessageComputer(); - const serializedMessage = messageComputer.computeBytesForVerifying(message); + // serialize the message for verification + const messageComputer = new MessageComputer(); + const serializedMessage = messageComputer.computeBytesForVerifying(message); - // verify the signature - const isSignedByAlice = aliceVerifier.verify(serializedMessage, message.signature); + // verify the signature + const isSignedByAlice = await aliceVerifier.verify(serializedMessage, message.signature); - console.log("Message is signed by Alice: ", isSignedByAlice); -} -``` + console.log("Message is signed by Alice: ", isSignedByAlice); + } + // ``` -#### Verifying a signature using a public key -```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const account = await Account.newFromPem(filePath); - - const transaction = new Transaction({ - nonce: 90n, - sender: account.address, - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - value: 1000000000000000000n, - gasLimit: 50000n, - chainID: "D" - }); - - // sign and apply the signature on the transaction - transaction.signature = await account.sign(transaction); - - // instantiating a public key - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const publicKey = new UserPublicKey(alice.getPublicKey()); - - // serialize the transaction for verification - const transactionComputer = new TransactionComputer(); - const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); - - // verify the signature - const isSignedByAlice = publicKey.verify(serializedTransaction, transaction.signature); - console.log("Transaction is signed by Alice: ", isSignedByAlice); -} -``` + // #### Verifying a signature using a public key + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + nonce: 90n, + sender: account.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D", + }); + + // sign and apply the signature on the transaction + transaction.signature = await account.signTransaction(transaction); + + // instantiating a public key + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const publicKey = new UserPublicKey(alice.getPublicKey()); + + // serialize the transaction for verification + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); + + // verify the signature + const isSignedByAlice = await publicKey.verify(serializedTransaction, transaction.signature); + console.log("Transaction is signed by Alice: ", isSignedByAlice); + } + // ``` -#### Sending messages over boundaries -Signed Message objects are typically sent to a remote party (e.g., a service), which can then verify the signature. -To prepare a message for transmission, you can use the `MessageComputer.packMessage()` utility method. + // #### Sending messages over boundaries + // Signed Message objects are typically sent to a remote party (e.g., a service), which can then verify the signature. + // To prepare a message for transmission, you can use the `MessageComputer.packMessage()` utility method. -```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const account = await Account.newFromPem(filePath); + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); - const message = new Message({ - data: new Uint8Array(Buffer.from("hello")), - address: account.address - }); + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: account.address, + }); - // sign and apply the signature on the message - message.signature = await account.sign(message); + // sign and apply the signature on the message + message.signature = await account.signMessage(message); - const messageComputer = new MessageComputer(); - const packedMessage = messageComputer.packMessage(message); + const messageComputer = new MessageComputer(); + const packedMessage = messageComputer.packMessage(message); - console.log("Packed message", packedMessage); -} -``` + console.log("Packed message", packedMessage); + } + // ``` -Then, on the receiving side, you can use [`MessageComputer.unpackMessage()`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/MessageComputer.html#unpackMessage) to reconstruct the message, prior verification: + // Then, on the receiving side, you can use [`MessageComputer.unpackMessage()`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/MessageComputer.html#unpackMessage) to reconstruct the message, prior verification: -```js -{ - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const messageComputer = new MessageComputer(); + const data = Buffer.from("test"); - const messageComputer = new MessageComputer(); + const message = new Message({ + data: data, + address: alice.address, + }); - // restore message - const message = messageComputer.unpackMessage(packedMessage); + message.signature = await alice.signMessage(message); + // restore message - // verify the signature - const publicKey = new UserPublicKey(alice.getPublicKey()); - const isSignedByAlice = publicKey.verify(messageComputer.computeBytesForVerifying(message), message.signature); + const packedMessage = messageComputer.packMessage(message); + const unpackedMessage = messageComputer.unpackMessage(packedMessage); - console.log("Transaction is signed by Alice: ", isSignedByAlice); -} -``` + // verify the signature + const isSignedByAlice = await alice.verifyMessageSignature(unpackedMessage, message.signature); + + console.log("Transaction is signed by Alice: ", isSignedByAlice); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/delegation.js b/cookbook/delegation.js deleted file mode 100644 index 37b92de4..00000000 --- a/cookbook/delegation.js +++ /dev/null @@ -1,383 +0,0 @@ -import { Account, Address, DevnetEntrypoint, TokenManagementTransactionsOutcomeParser } from "@multiversx/sdk-core"; // md-ignore -import path from 'path'; // md-ignore -// ### Delegation management - -// To learn more about staking providers and delegation, please refer to the official [documentation](https://docs.multiversx.com/validators/delegation-manager/#introducing-staking-providers). -// In this section, we'll cover how to: -// - Create a new delegation contract -// - Retrieve the contract address -// - Delegate funds to the contract -// - Redelegate rewards -// - Claim rewards -// - Undelegate and withdraw funds - -// These operations can be performed using both the controller and the **factory**. For a complete list of supported methods, please refer to the autogenerated documentation: -// - [DelegationController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationController.html) -// - [DelegationTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationTransactionsFactory.html) - -// #### Creating a New Delegation Contract Using the Controller -// ```js -{ - // create the entrypoint and the delegation controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = await controller.createTransactionForNewDelegationContract( - alice.address, - alice.getNonceThenIncrement(), - { - totalDelegationCap: 0, - serviceFee: 10n, - amount: 1250000000000000000000n, - }); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction completion, extract delegation contract's address // md-as-comment - const outcome = await controller.awaitCompletedCreateNewDelegationContract(txHash); - - const contractAddress = outcome[0].contractAddress; -} -// ``` - -// #### Creating a new delegation contract using the factory -// ```js -{ - // create the entrypoint and the delegation factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - - const transaction = await factory.createTransactionForNewDelegationContract(alice.address, - { - totalDelegationCap: 0, - serviceFee: 10n, - amount: 1250000000000000000000n, - }); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // set the nonce // md-as-comment - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); - - // waits until the transaction is processed and fetches it from the network // md-as-comment - const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - - // extract the contract address // md-as-comment - const parser = new TokenManagementTransactionsOutcomeParser(); - const outcome = parser.parseIssueFungible(transactionOnNetwork); - const contractAddress = outcome[0].contractAddress; -} -// ``` - -// #### Delegating funds to the contract using the Controller -// We can send funds to a delegation contract to earn rewards. - -// ```js -{ - // create the entrypoint and the delegation controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - - const transaction = await controller.createTransactionForDelegating( - alice.address, - alice.getNonceThenIncrement(), - { - delegationContract: contract, - amount: 5000000000000000000000n, - }); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Delegating funds to the contract using the factory -// ```js -{ - // create the entrypoint and the delegation factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - - const transaction = await factory.createTransactionForDelegating(alice.address, - { - delegationContract: contract, - amount: 5000000000000000000000n, - }); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // set the nonce // md-as-comment - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Redelegating rewards using the Controller -// Over time, as rewards accumulate, we may choose to redelegate them to the contract to maximize earnings. - -// ```js -{ - // create the entrypoint and the delegation controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = await controller.createTransactionForRedelegatingRewards( - alice.address, - alice.getNonceThenIncrement(), - { - delegationContract: contract, - }); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Redelegating rewards using the factory -// ```js -{ - // create the entrypoint and the delegation factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - - const transaction = await factory.createTransactionForRedelegatingRewards(alice.address, - { - delegationContract: contract, - }); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // set the nonce // md-as-comment - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Claiming rewards using the Controller -// We can also claim our rewards when needed. - -// ```js -{ - // create the entrypoint and the delegation controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = await controller.createTransactionForClaimingRewards( - alice.address, - alice.getNonceThenIncrement(), - { - delegationContract: contract, - }); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Claiming rewards using the factory -// ```js -{ - // create the entrypoint and the delegation factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - - const transaction = await factory.createTransactionForClaimingRewards(alice.address, - { - delegationContract: contract, - }); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // set the nonce // md-as-comment - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Undelegating funds using the Controller -// By **undelegating**, we signal the contract that we want to retrieve our staked funds. This process requires a **10-epoch unbonding period** before the funds become available. - -// ```js -{ - // create the entrypoint and the delegation controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = await controller.createTransactionForUndelegating( - alice.address, - alice.getNonceThenIncrement(), - { - delegationContract: contract, - amount: 1000000000000000000000n // 1000 EGLD // md-as-comment - }); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Undelegating funds using the factory -// ```js -{ - // create the entrypoint and the delegation factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - - const transaction = await factory.createTransactionForUndelegating(alice.address, - { - delegationContract: contract, - amount: 1000000000000000000000n // 1000 EGLD // md-as-comment - }); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // set the nonce // md-as-comment - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Withdrawing funds using the Controller -// After the `10-epoch unbonding period` is complete, we can proceed with withdrawing our staked funds using the controller. This final step allows us to regain access to the previously delegated funds. - -// ```js -{ - // create the entrypoint and the delegation controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = await controller.createTransactionForWithdrawing( - alice.address, - alice.getNonceThenIncrement(), - { - delegationContract: contract, - }); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Withdrawing funds using the factory -// ```js -{ - // create the entrypoint and the delegation factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - - const transaction = await factory.createTransactionForWithdrawing(alice.address, - { - delegationContract: contract, - }); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // set the nonce // md-as-comment - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` \ No newline at end of file diff --git a/cookbook/delegation.ts b/cookbook/delegation.ts new file mode 100644 index 00000000..f667fe09 --- /dev/null +++ b/cookbook/delegation.ts @@ -0,0 +1,370 @@ +import path from "path"; // md-ignore +import { Account, Address, DevnetEntrypoint, TokenManagementTransactionsOutcomeParser } from "../src"; // md-ignore +(async () => { + // ### Delegation management + + // To learn more about staking providers and delegation, please refer to the official [documentation](https://docs.multiversx.com/validators/delegation-manager/#introducing-staking-providers). + // In this section, we'll cover how to: + // - Create a new delegation contract + // - Retrieve the contract address + // - Delegate funds to the contract + // - Redelegate rewards + // - Claim rewards + // - Undelegate and withdraw funds + + // These operations can be performed using both the controller and the **factory**. For a complete list of supported methods, please refer to the autogenerated documentation: + // - [DelegationController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationController.html) + // - [DelegationTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationTransactionsFactory.html) + + // #### Creating a New Delegation Contract Using the Controller + // ```js + { + // create the entrypoint and the delegation controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForNewDelegationContract( + alice, + alice.getNonceThenIncrement(), + { + totalDelegationCap: 0n, + serviceFee: 10n, + amount: 1250000000000000000000n, + }, + ); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction completion, extract delegation contract's address // md-as-comment + const outcome = await controller.awaitCompletedCreateNewDelegationContract(txHash); + + const contractAddress = outcome[0].contractAddress; + } + // ``` + + // #### Creating a new delegation contract using the factory + // ```js + { + // create the entrypoint and the delegation factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForNewDelegationContract(alice.address, { + totalDelegationCap: 0n, + serviceFee: 10n, + amount: 1250000000000000000000n, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // waits until the transaction is processed and fetches it from the network // md-as-comment + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier // md-as-comment + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueFungible(transactionOnNetwork); + const tokenIdentifier = outcome[0].tokenIdentifier; + } + // ``` + + // #### Delegating funds to the contract using the Controller + // We can send funds to a delegation contract to earn rewards. + + // ```js + { + // create the entrypoint and the delegation controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await controller.createTransactionForDelegating(alice, alice.getNonceThenIncrement(), { + delegationContract: contract, + amount: 5000000000000000000000n, + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Delegating funds to the contract using the factory + // ```js + { + // create the entrypoint and the delegation factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForDelegating(alice.address, { + delegationContract: contract, + amount: 5000000000000000000000n, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Redelegating rewards using the Controller + // Over time, as rewards accumulate, we may choose to redelegate them to the contract to maximize earnings. + + // ```js + { + // create the entrypoint and the delegation controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForRedelegatingRewards( + alice, + alice.getNonceThenIncrement(), + { + delegationContract: contract, + }, + ); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Redelegating rewards using the factory + // ```js + { + // create the entrypoint and the delegation factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForRedelegatingRewards(alice.address, { + delegationContract: contract, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Claiming rewards using the Controller + // We can also claim our rewards when needed. + + // ```js + { + // create the entrypoint and the delegation controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForClaimingRewards(alice, alice.getNonceThenIncrement(), { + delegationContract: contract, + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Claiming rewards using the factory + // ```js + { + // create the entrypoint and the delegation factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForClaimingRewards(alice.address, { + delegationContract: contract, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Undelegating funds using the Controller + // By **undelegating**, we signal the contract that we want to retrieve our staked funds. This process requires a **10-epoch unbonding period** before the funds become available. + + // ```js + { + // create the entrypoint and the delegation controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForUndelegating(alice, alice.getNonceThenIncrement(), { + delegationContract: contract, + amount: 1000000000000000000000n, // 1000 EGLD // md-as-comment + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Undelegating funds using the factory + // ```js + { + // create the entrypoint and the delegation factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForUndelegating(alice.address, { + delegationContract: contract, + amount: 1000000000000000000000n, // 1000 EGLD // md-as-comment + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Withdrawing funds using the Controller + // After the `10-epoch unbonding period` is complete, we can proceed with withdrawing our staked funds using the controller. This final step allows us to regain access to the previously delegated funds. + + // ```js + { + // create the entrypoint and the delegation controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForWithdrawing(alice, alice.getNonceThenIncrement(), { + delegationContract: contract, + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Withdrawing funds using the factory + // ```js + { + // create the entrypoint and the delegation factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForWithdrawing(alice.address, { + delegationContract: contract, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce // md-as-comment + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/entrypoints.js b/cookbook/entrypoints.js deleted file mode 100644 index 5ab8539e..00000000 --- a/cookbook/entrypoints.js +++ /dev/null @@ -1,34 +0,0 @@ -import { DevnetEntrypoint } from "@multiversx/sdk-core"; // md-ignore -// ## Overview - -// This guide walks you through handling common tasks using the MultiversX Javascript SDK (v14, latest stable version). - -// ## Creating an Entrypoint - -// An Entrypoint represents a network client that simplifies access to the most common operations. -// There is a dedicated entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint`, `LocalnetEntrypoint`. - -// For example, to create a Devnet entrypoint you have the following command: - -// ```js -const entrypoint = new DevnetEntrypoint(); -// ``` - -// #### Using a Custom API -// If you'd like to connect to a third-party API, you can specify the url parameter: - -// ```js -const apiEntrypoint = new DevnetEntrypoint({ url: 'https://custom-multiversx-devnet-api.com' }); -// ``` - -// #### Using a Proxy - -// By default, the DevnetEntrypoint uses the standard API. However, you can create a custom entrypoint that interacts with a proxy by specifying the kind parameter: - - -// ```js -const customEntrypoint = new DevnetEntrypoint({ - url: 'https://devnet-gateway.multiversx.com', - kind: 'proxy' -}); -// ``` diff --git a/cookbook/entrypoints.ts b/cookbook/entrypoints.ts new file mode 100644 index 00000000..163ad031 --- /dev/null +++ b/cookbook/entrypoints.ts @@ -0,0 +1,34 @@ +import { DevnetEntrypoint } from "../src"; // md-ignore +(async () => { + // ## Overview + + // This guide walks you through handling common tasks using the MultiversX Javascript SDK (v14, latest stable version). + + // ## Creating an Entrypoint + + // An Entrypoint represents a network client that simplifies access to the most common operations. + // There is a dedicated entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint`, `LocalnetEntrypoint`. + + // For example, to create a Devnet entrypoint you have the following command: + + // ```js + const entrypoint = new DevnetEntrypoint(); + // ``` + + // #### Using a Custom API + // If you'd like to connect to a third-party API, you can specify the url parameter: + + // ```js + const apiEntrypoint = new DevnetEntrypoint("https://custom-multiversx-devnet-api.com"); + // ``` + + // #### Using a Proxy + + // By default, the DevnetEntrypoint uses the standard API. However, you can create a custom entrypoint that interacts with a proxy by specifying the kind parameter: + + // ```js + const customEntrypoint = new DevnetEntrypoint("https://devnet-gateway.multiversx.com", "proxy"); + // ``` +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/generate.py b/cookbook/generate.py index d6ab3e52..88170254 100644 --- a/cookbook/generate.py +++ b/cookbook/generate.py @@ -5,20 +5,20 @@ current_dir = Path(__file__).parent.absolute() input_files = [ - current_dir / "entrypoints.js", - current_dir / "account.js", - current_dir / "networkProviders.js", - current_dir / "transactions.js", - current_dir / "smartContracts.js", - current_dir / "tokens.js", - current_dir / "accountManagement.js", - current_dir / "delegation.js", - current_dir / "relayed.js", - current_dir / "guarded.js", - current_dir / "addresses.js", - current_dir / "wallets.js", - current_dir / "signingObjects.js", - current_dir / "verifySignatures.js" + current_dir / "entrypoints.ts", + current_dir / "account.ts", + current_dir / "networkProviders.ts", + current_dir / "transactions.ts", + current_dir / "smartContracts.ts", + current_dir / "tokens.ts", + current_dir / "accountManagement.ts", + current_dir / "delegation.ts", + current_dir / "relayed.ts", + current_dir / "guarded.ts", + current_dir / "addresses.ts", + current_dir / "wallets.ts", + current_dir / "signingObjects.ts", + current_dir / "verifySignatures.ts" ] MARKER_INSERT = "md-insert:" @@ -33,16 +33,6 @@ DOCS_URL = "https://docs.multiversx.com" notes: Dict[str, str] = { - "transactionLegacyVsNext": """:::note -Since `sdk-core v13`, the `class:Transaction` class exhibits its state as public read-write properties. For example, you can access and set the `nonce` property, instead of using `getNonce` and `setNonce`. -:::""", - - "forSimplicityWeUseUserSigner": f""":::important -For the sake of simplicity, in this section we'll use a `UserSigner` object to sign the transaction. -In real-world dApps, transactions are signed by end-users using their wallet, through a [signing provider]({DOCS_URL}/sdk-and-tools/sdk-js/sdk-js-signing-providers). -::: -""", - "mixedTypedValuesAndNativeValues": """:::tip When creating transactions using `class:SmartContractController` or `class:SmartContractTransactionsFactory`, even if the ABI is available and provided, you can still use `class:TypedValue` objects as arguments for deployments and interactions. diff --git a/cookbook/guarded.js b/cookbook/guarded.js deleted file mode 100644 index e70723d7..00000000 --- a/cookbook/guarded.js +++ /dev/null @@ -1,130 +0,0 @@ -import { Account, DevnetEntrypoint } from "@multiversx/sdk-core"; // md-ignore -import path from 'path'; // md-ignore -// ### Guarded transactions -// Similar to relayers, transactions also have two additional fields: - -// - guardian -// - guardianSignature - -// Each controller includes an argument for the guardian. The transaction can either: -// 1. Be sent to a service that signs it using the guardian’s account, or -// 2. Be signed by another account acting as a guardian. - -// Let’s issue a token using a guarded account: - -// #### Creating guarded transactions using controllers -// We can create guarded transactions using any of the available controllers. - -// Each controller method includes a guardian argument, which must be set if we want to create a guarded transaction. -// Let’s issue a fungible token using a relayed transaction: - -// ```js -{ - // create the entrypoint and the token management controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.creatTokenManagementController(); - - // create the issuer of the token // md-as-comment - const walletsPath = path.join("src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); - - // carol will be our guardian // md-as-comment - const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = await controller.createTransactionForIssuingFungible( - alice, - alice.getNonceThenIncrement(), - { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - guardian: carol.address, - }, - ); - - // guardian also signs the transaction // md-as-comment - transaction.guardianSignature = await carol.signTransaction(transaction); - - // broadcast the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Creating guarded transactions using factories -// Unlike controllers, `transaction factories` do not have a `guardian` argument. Instead, the **guardian must be set after creating the transaction**. -// This approach is beneficial because the transaction is **not signed by the sender at the time of creation**, allowing flexibility in setting the guardian before signing. - -// Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: - -// ```js -{ - // create the entrypoint and the token management factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementController(); - - // create the issuer of the token // md-as-comment - const walletsPath = path.join("src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); - - // carol will be our guardian // md-as-comment - const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); - - const transaction = await factory.createTransactionForIssuingFungible( - alice.address, - { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - }, - ); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // set the guardian // md-as-comment - transaction.guardian = carol.address; - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // guardian also signs the transaction // md-as-comment - transaction.guardianSignature = await carol.signTransaction(transaction); - - // broadcast the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// We can create guarded relayed transactions just like we did before. However, keep in mind: - -// Only the sender can be guarded, the relayer cannot be guarded. - -// Flow for Creating Guarded Relayed Transactions: -// - Using Controllers: -// 1. Set both guardian and relayer fields. -// 2. The transaction must be signed by both the guardian and the relayer. -// - Using Factories: - -// 1. Create the transaction. -// 2. Set both guardian and relayer fields. -// 3. First, the sender signs the transaction. -// 4. Then, the guardian signs. -// 5. Finally, the relayer signs before broadcasting. \ No newline at end of file diff --git a/cookbook/guarded.ts b/cookbook/guarded.ts new file mode 100644 index 00000000..687c34b0 --- /dev/null +++ b/cookbook/guarded.ts @@ -0,0 +1,127 @@ +import path from "path"; // md-ignore +import { Account, DevnetEntrypoint } from "../src"; // md-ignore +(async () => { + // ### Guarded transactions + // Similar to relayers, transactions also have two additional fields: + + // - guardian + // - guardianSignature + + // Each controller includes an argument for the guardian. The transaction can either: + // 1. Be sent to a service that signs it using the guardian’s account, or + // 2. Be signed by another account acting as a guardian. + + // Let’s issue a token using a guarded account: + + // #### Creating guarded transactions using controllers + // We can create guarded transactions using any of the available controllers. + + // Each controller method includes a guardian argument, which must be set if we want to create a guarded transaction. + // Let’s issue a fungible token using a relayed transaction: + + // ```js + { + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token // md-as-comment + const walletsPath = path.join("../src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // carol will be our guardian // md-as-comment + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingFungible(alice, alice.getNonceThenIncrement(), { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + guardian: carol.address, + }); + + // guardian also signs the transaction // md-as-comment + transaction.guardianSignature = await carol.signTransaction(transaction); + + // broadcast the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Creating guarded transactions using factories + // Unlike controllers, `transaction factories` do not have a `guardian` argument. Instead, the **guardian must be set after creating the transaction**. + // This approach is beneficial because the transaction is **not signed by the sender at the time of creation**, allowing flexibility in setting the guardian before signing. + + // Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: + + // ```js + { + // create the entrypoint and the token management factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token // md-as-comment + const walletsPath = path.join("../src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // carol will be our guardian // md-as-comment + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + + const transaction = await factory.createTransactionForIssuingFungible(alice.address, { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // set the guardian // md-as-comment + transaction.guardian = carol.address; + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // guardian also signs the transaction // md-as-comment + transaction.guardianSignature = await carol.signTransaction(transaction); + + // broadcast the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // We can create guarded relayed transactions just like we did before. However, keep in mind: + + // Only the sender can be guarded, the relayer cannot be guarded. + + // Flow for Creating Guarded Relayed Transactions: + // - Using Controllers: + // 1. Set both guardian and relayer fields. + // 2. The transaction must be signed by both the guardian and the relayer. + // - Using Factories: + + // 1. Create the transaction. + // 2. Set both guardian and relayer fields. + // 3. First, the sender signs the transaction. + // 4. Then, the guardian signs. + // 5. Finally, the relayer signs before broadcasting. +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/networkProviders.js b/cookbook/networkProviders.js deleted file mode 100644 index 1f18f891..00000000 --- a/cookbook/networkProviders.js +++ /dev/null @@ -1,447 +0,0 @@ -import { Address, ApiNetworkProvider, DevnetEntrypoint, ProxyNetworkProvider, Token, Transaction } from "@multiversx/sdk-core"; // md-ignore -// ## Calling the Faucet - -// This functionality is not yet available through the entrypoint, but we recommend using the faucet available within the Web Wallet. For more details about the faucet [see this](https://docs.multiversx.com/wallet/web-wallet/#testnet-and-devnet-faucet). - -// - [Testnet Wallet](https://testnet-wallet.multiversx.com/). -// - [Devnet Wallet](https://devnet-wallet.multiversx.com/). - -// ### Interacting with the network - -// The entrypoint exposes a few ways to directly interact with the network, such as: - -// - `recallAccountNonce(address: Address): Promise;` -// - `sendTransactions(transactions: Transaction[]): Promise<[number, string[]]>;` -// - `sendTransaction(transaction: Transaction): Promise;` -// - `getTransaction(txHash: string): Promise;` -// - `awaitCompletedTransaction(txHash: string): Promise;` - -// Some other methods are exposed through a so called **network provider**. - -// - **ApiNetworkProvider**: Interacts with the API, which is a layer over the proxy. It fetches data from the network and `Elastic Search`. -// - **ProxyNetworkProvider**: Interacts directly with the proxy of an observing squad. - -// To get the underlying network provider from our entrypoint, we can do as follows: - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const networkProvider = entrypoint.createNetworkProvider(); -} -// ``` - -// ### Creating a network provider -// When manually instantiating a network provider, you can provide a configuration to specify the client name and set custom request options. - -// ```js -{ - // Create a configuration object - const config = { - clientName: "hello-multiversx", - requestsOptions: { - timeout: 1000, // Timeout in milliseconds // md-as-comment - auth: { - username: "user", - password: "password" - } - } - }; - - // Instantiate the network provider with the config - const api = new ApiNetworkProvider("https://devnet-api.multiversx.com", config); -} -// ``` - -// A full list of available methods for `ApiNetworkProvider` can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/classes/ApiNetworkProvider.html). - -// Both `ApiNetworkProvider` and `ProxyNetworkProvider` implement a common interface, which can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/interfaces/INetworkProvider.html). This allows them to be used interchangeably. - -// The classes returned by the API expose the most commonly used fields directly for convenience. However, each object also contains a `raw` field that stores the original API response, allowing access to additional fields if needed. - - -// ## Fetching data from the network - -// ### Fetching the network config - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const networkProvider = entrypoint.createNetworkProvider(); - - const networkConfig = entrypoint.getNetworkConfig(); -} -// ``` - -// ### Fetching the network status - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const networkProvider = entrypoint.createNetworkProvider(); - - const metaNetworkStatus = entrypoint.getNetworkStatus(); // fetches status from metachain // md-as-comment - const networkStatus = entrypoint.getNetworkStatus(1); // fetches status from shard one // md-as-comment -} -// ``` - -// ### Fetching a Block from the Network -// To fetch a block, we first instantiate the required arguments and use its hash. The API only supports fetching blocks by hash, whereas the **PROXY** allows fetching blocks by either hash or nonce. - -// When using the **PROXY**, keep in mind that the shard must also be specified in the arguments. - -// #### Fetching a block using the **API** -// ```js -{ - const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); - const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; - const block = await api.getBlock(blockHash); -} -// ``` - -// Additionally, we can fetch the latest block from the network: - -// ```js -{ - const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); - const latestBlock = await api.getLatestBlock(); -} -// ``` - -// #### Fetching a block using the **PROXY** - -// When using the proxy, we have to provide the shard, as well. -// ```js -{ - const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); - const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; - const block = proxy.getBlock({ blockHash, shard: 1 }); -} -// ``` - -// We can also fetch the latest block from the network. -// By default, the shard will be the metachain, but we can specify a different shard if needed. - -// ```js -{ - const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); - const latestBlock = proxy.getLatestBlock(); -} -// ``` - -// ### Fetching an Account -// To fetch an account, we need its address. Once we have the address, we create an `Address` object and pass it as an argument to the method. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const account = await api.getAccount(alice); -} -// ``` - -// ### Fetching an Account's Storage -// We can also fetch an account's storage, allowing us to retrieve all key-value pairs saved for that account. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const account = await api.getAccountStorage(alice); -} -// ``` - -// If we only want to fetch a specific key, we can do so as follows: - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const account = await api.getAccountStorageEntry(alice, "testKey"); -} -// ``` - -// ### Waiting for an Account to Meet a Condition -// There are times when we need to wait for a specific condition to be met before proceeding with an action. -// For example, let's say we want to send 7 EGLD from Alice to Bob, but this can only happen once Alice's balance reaches at least 7 EGLD. -// This approach is useful in scenarios where you're waiting for external funds to be sent to Alice, enabling her to transfer the required amount to another recipient. - -// To implement this, we need to define the condition to check each time the account is fetched from the network. We create a function that takes an `AccountOnNetwork` object as an argument and returns a `bool`. -// Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const condition = (account) => { - return account.balance >= 7000000000000000000; // 7 EGLD // md-as-comment - }; - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const account = await api.awaitAccountOnCondition(alice, condition); -} -// ``` - -// ### Sending and Simulating Transactions -// To execute transactions, we use the network providers to broadcast them to the network. Keep in mind that for transactions to be processed, they must be signed. - -// #### Sending a Transaction - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - const transaction = new Transaction({ - sender: alice, - receiver: bob, - gasLimit: 50000n, - chainID: "D", - }); - - // set the correct nonce and sign the transaction ... - - const transactionHash = await api.sendTransaction(transaction); -} -// ``` - -// #### Sending multiple transactions -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - const firstTransaction = new Transaction({ - sender: alice, - receiver: bob, - gasLimit: 50000n, - chainID: "D", - nonce: 2 - }); - - const secondTransaction = new Transaction({ - sender: bob, - receiver: alice, - gasLimit: 50000n, - chainID: "D", - nonce: 1, - }); - - const thirdTransaction = new Transaction({ - sender: alice, - receiver: alice, - gasLimit: 60000n, - chainID: "D", - nonce: 3, - data: new Uint8Array(Buffer.from("hello")) - }); - - // set the correct nonce and sign the transaction ... - - const [numOfSentTxs, hashes] = await api.sendTransactions([firstTransaction, secondTransaction, thirdTransaction]); -} -// ``` - -// #### Simulating transactions -// A transaction can be simulated before being sent for processing by the network. This is primarily used for smart contract calls, allowing you to preview the results produced by the smart contract. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); - - const transaction = new Transaction({ - sender: alice, - receiver: contract, - gasLimit: 5000000n, - chainID: "D", - data: new Uint8Array(Buffer.from("add@07")) - }); - - const transactionOnNetwork = await api.simulateTransaction(transaction); -} -// ``` - -// #### Estimating the gas cost of a transaction -// Before sending a transaction to the network for processing, you can retrieve the estimated gas limit required for the transaction to be executed. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); - - const nonce = await entrypoint.recallAccountNonce(alice); - - const transaction = new Transaction({ - sender: alice, - receiver: contract, - gasLimit: 5000000, - chainID: "D", - data: new Uint8Array(Buffer.from("add@07")), - nonce: nonce - }); - - const transactionCostResponse = await api.estimateTransactionCost(transaction); -} -// ``` - -// ### Waiting for transaction completion -// After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const txHash = "exampletransactionhash"; - const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); -} -// ``` - -// ### Waiting for a Transaction to Satisfy a Condition -// Similar to accounts, we can wait until a transaction meets a specific condition. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const condition = (txOnNetwork) => !txOnNetwork.status.isSuccessful(); - - const txHash = "exampletransactionhash"; - const transactionOnNetwork = await api.awaitTransactionOnCondition(txHash, condition); -} -// ``` - -// ### Waiting for transaction completion -// After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const txHash = "exampletransactionhash"; - const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); -} -// ``` - -// ### Fetching Transactions from the Network -// After sending a transaction, we can fetch it from the network using the transaction hash, which we receive after broadcasting the transaction. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const txHash = "exampletransactionhash"; - const transactionOnNetwork = await api.getTransaction(txHash); -} -// ``` - -// ### Fetching a token from an account -// We can fetch a specific token (ESDT, MetaESDT, SFT, NFT) from an account by providing the account's address and the token identifier. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - let token = new Token({ identifier: "TEST-ff155e" }); // ESDT // md-as-comment - let tokenOnNetwork = await api.getTokenOfAccount(alice, token); - - - token = new Token({ identifier: "NFT-987654", nonce: 11n }); // NFT // md-as-comment - tokenOnNetwork = await api.getTokenOfAccount(alice, token); -} -// ``` - -// ### Fetching all fungible tokens of an account -// Fetches all fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const fungibleTokens = await api.getFungibleTokensOfAccount(alice); -} -// ``` - -// ### Fetching all non-fungible tokens of an account -// Fetches all non-fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const nfts = await api.getNonFungibleTokensOfAccount(alice); -} -// ``` - -// ### Fetching token metadata -// If we want to fetch the metadata of a token (e.g., owner, decimals, etc.), we can use the following methods: - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - // used for ESDT // md-as-comment - const fungibleTokenDefinition = await api.getDefinitionOfFungibleToken("TEST-ff155e"); - - // used for METAESDT, SFT, NFT // md-as-comment - const nonFungibleTokenDefinition = await api.getDefinitionOfTokenCollection("NFTEST-ec88b8"); -} -// ``` - -// ### Querying Smart Contracts -// Smart contract queries, or view functions, are endpoints that only read data from the contract. To send a query to the observer nodes, we can proceed as follows: - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const query = new SmartContractQuery({ - contract: Address.newFromBech32("erd1qqqqqqqqqqqqqpgqqy34h7he2ya6qcagqre7ur7cc65vt0mxrc8qnudkr4"), - function: "getSum", - arguments: [], - }); - const response = await api.queryContract(query); -} -// ``` - -// ### Custom Api/Proxy calls -// The methods exposed by the `ApiNetworkProvider` or `ProxyNetworkProvider` are the most common and widely used. However, there may be times when custom API calls are needed. For these cases, we’ve created generic methods for both GET and POST requests. -// Let’s assume we want to retrieve all the transactions sent by Alice in which the `delegate` function was called. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const url = `transactions/${alice.toBech32()}?function=delegate`; - - const response = await api.doGetGeneric(url); -} -// ``` \ No newline at end of file diff --git a/cookbook/networkProviders.ts b/cookbook/networkProviders.ts new file mode 100644 index 00000000..309a354f --- /dev/null +++ b/cookbook/networkProviders.ts @@ -0,0 +1,461 @@ +import { + Address, + ApiNetworkProvider, + DevnetEntrypoint, + ProxyNetworkProvider, + SmartContractQuery, + Token, + Transaction, +} from "../src"; // md-ignore +(async () => { + // ## Calling the Faucet + + // This functionality is not yet available through the entrypoint, but we recommend using the faucet available within the Web Wallet. For more details about the faucet [see this](https://docs.multiversx.com/wallet/web-wallet/#testnet-and-devnet-faucet). + + // - [Testnet Wallet](https://testnet-wallet.multiversx.com/). + // - [Devnet Wallet](https://devnet-wallet.multiversx.com/). + + // ### Interacting with the network + + // The entrypoint exposes a few ways to directly interact with the network, such as: + + // - `recallAccountNonce(address: Address): Promise;` + // - `sendTransactions(transactions: Transaction[]): Promise<[number, string[]]>;` + // - `sendTransaction(transaction: Transaction): Promise;` + // - `getTransaction(txHash: string): Promise;` + // - `awaitCompletedTransaction(txHash: string): Promise;` + + // Some other methods are exposed through a so called **network provider**. + + // - **ApiNetworkProvider**: Interacts with the API, which is a layer over the proxy. It fetches data from the network and `Elastic Search`. + // - **ProxyNetworkProvider**: Interacts directly with the proxy of an observing squad. + + // To get the underlying network provider from our entrypoint, we can do as follows: + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); + } + // ``` + + // ### Creating a network provider + // When manually instantiating a network provider, you can provide a configuration to specify the client name and set custom request options. + + // ```js + { + // Create a configuration object + const config = { + clientName: "hello-multiversx", + requestsOptions: { + timeout: 1000, // Timeout in milliseconds // md-as-comment + auth: { + username: "user", + password: "password", + }, + }, + }; + + // Instantiate the network provider with the config + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com", config); + } + // ``` + + // A full list of available methods for `ApiNetworkProvider` can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/classes/ApiNetworkProvider.html). + + // Both `ApiNetworkProvider` and `ProxyNetworkProvider` implement a common interface, which can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/interfaces/INetworkProvider.html). This allows them to be used interchangeably. + + // The classes returned by the API expose the most commonly used fields directly for convenience. However, each object also contains a `raw` field that stores the original API response, allowing access to additional fields if needed. + + // ## Fetching data from the network + + // ### Fetching the network config + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); + + const networkConfig = networkProvider.getNetworkConfig(); + } + // ``` + + // ### Fetching the network status + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); + + const metaNetworkStatus = networkProvider.getNetworkStatus(); // fetches status from metachain // md-as-comment + const networkStatus = networkProvider.getNetworkStatus(); // fetches status from shard one // md-as-comment + } + // ``` + + // ### Fetching a Block from the Network + // To fetch a block, we first instantiate the required arguments and use its hash. The API only supports fetching blocks by hash, whereas the **PROXY** allows fetching blocks by either hash or nonce. + + // When using the **PROXY**, keep in mind that the shard must also be specified in the arguments. + + // #### Fetching a block using the **API** + // ```js + { + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); + const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; + const block = await api.getBlock(blockHash); + } + // ``` + + // Additionally, we can fetch the latest block from the network: + + // ```js + { + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); + const latestBlock = await api.getLatestBlock(); + } + // ``` + + // #### Fetching a block using the **PROXY** + + // When using the proxy, we have to provide the shard, as well. + // ```js + { + const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); + const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; + const block = proxy.getBlock({ blockHash, shard: 1 }); + } + // ``` + + // We can also fetch the latest block from the network. + // By default, the shard will be the metachain, but we can specify a different shard if needed. + + // ```js + { + const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); + const latestBlock = proxy.getLatestBlock(); + } + // ``` + + // ### Fetching an Account + // To fetch an account, we need its address. Once we have the address, we create an `Address` object and pass it as an argument to the method. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccount(alice); + } + // ``` + + // ### Fetching an Account's Storage + // We can also fetch an account's storage, allowing us to retrieve all key-value pairs saved for that account. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccountStorage(alice); + } + // ``` + + // If we only want to fetch a specific key, we can do so as follows: + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccountStorageEntry(alice, "testKey"); + } + // ``` + + // ### Waiting for an Account to Meet a Condition + // There are times when we need to wait for a specific condition to be met before proceeding with an action. + // For example, let's say we want to send 7 EGLD from Alice to Bob, but this can only happen once Alice's balance reaches at least 7 EGLD. + // This approach is useful in scenarios where you're waiting for external funds to be sent to Alice, enabling her to transfer the required amount to another recipient. + + // To implement this, we need to define the condition to check each time the account is fetched from the network. We create a function that takes an `AccountOnNetwork` object as an argument and returns a `bool`. + // Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const condition = (account: any) => { + return account.balance >= 7000000000000000000n; // 7 EGLD // md-as-comment + }; + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.awaitAccountOnCondition(alice, condition); + } + // ``` + + // ### Sending and Simulating Transactions + // To execute transactions, we use the network providers to broadcast them to the network. Keep in mind that for transactions to be processed, they must be signed. + + // #### Sending a Transaction + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = new Transaction({ + sender: alice, + receiver: bob, + gasLimit: 50000n, + chainID: "D", + }); + + // set the correct nonce and sign the transaction ... + + const transactionHash = await api.sendTransaction(transaction); + } + // ``` + + // #### Sending multiple transactions + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const firstTransaction = new Transaction({ + sender: alice, + receiver: bob, + gasLimit: 50000n, + chainID: "D", + nonce: 2n, + }); + + const secondTransaction = new Transaction({ + sender: bob, + receiver: alice, + gasLimit: 50000n, + chainID: "D", + nonce: 1n, + }); + + const thirdTransaction = new Transaction({ + sender: alice, + receiver: alice, + gasLimit: 60000n, + chainID: "D", + nonce: 3n, + data: new Uint8Array(Buffer.from("hello")), + }); + + // set the correct nonce and sign the transaction ... + + const [numOfSentTxs, hashes] = await api.sendTransactions([ + firstTransaction, + secondTransaction, + thirdTransaction, + ]); + } + // ``` + + // #### Simulating transactions + // A transaction can be simulated before being sent for processing by the network. This is primarily used for smart contract calls, allowing you to preview the results produced by the smart contract. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); + + const transaction = new Transaction({ + sender: alice, + receiver: contract, + gasLimit: 5000000n, + chainID: "D", + data: new Uint8Array(Buffer.from("add@07")), + }); + + const transactionOnNetwork = await api.simulateTransaction(transaction); + } + // ``` + + // #### Estimating the gas cost of a transaction + // Before sending a transaction to the network for processing, you can retrieve the estimated gas limit required for the transaction to be executed. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); + + const nonce = await entrypoint.recallAccountNonce(alice); + + const transaction = new Transaction({ + sender: alice, + receiver: contract, + gasLimit: 5000000n, + chainID: "D", + data: new Uint8Array(Buffer.from("add@07")), + nonce: nonce, + }); + + const transactionCostResponse = await api.estimateTransactionCost(transaction); + } + // ``` + + // ### Waiting for transaction completion + // After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); + } + // ``` + + // ### Waiting for a Transaction to Satisfy a Condition + // Similar to accounts, we can wait until a transaction meets a specific condition. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const condition = (txOnNetwork: any) => !txOnNetwork.status.isSuccessful(); + + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionOnCondition(txHash, condition); + } + // ``` + + // ### Waiting for transaction completion + // After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); + } + // ``` + + // ### Fetching Transactions from the Network + // After sending a transaction, we can fetch it from the network using the transaction hash, which we receive after broadcasting the transaction. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.getTransaction(txHash); + } + // ``` + + // ### Fetching a token from an account + // We can fetch a specific token (ESDT, MetaESDT, SFT, NFT) from an account by providing the account's address and the token identifier. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + let token = new Token({ identifier: "TEST-ff155e" }); // ESDT // md-as-comment + let tokenOnNetwork = await api.getTokenOfAccount(alice, token); + + token = new Token({ identifier: "NFT-987654", nonce: 11n }); // NFT // md-as-comment + tokenOnNetwork = await api.getTokenOfAccount(alice, token); + } + // ``` + + // ### Fetching all fungible tokens of an account + // Fetches all fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const fungibleTokens = await api.getFungibleTokensOfAccount(alice); + } + // ``` + + // ### Fetching all non-fungible tokens of an account + // Fetches all non-fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const nfts = await api.getNonFungibleTokensOfAccount(alice); + } + // ``` + + // ### Fetching token metadata + // If we want to fetch the metadata of a token (e.g., owner, decimals, etc.), we can use the following methods: + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + // used for ESDT // md-as-comment + const fungibleTokenDefinition = await api.getDefinitionOfFungibleToken("TEST-ff155e"); + + // used for METAESDT, SFT, NFT // md-as-comment + const nonFungibleTokenDefinition = await api.getDefinitionOfTokenCollection("NFTEST-ec88b8"); + } + // ``` + + // ### Querying Smart Contracts + // Smart contract queries, or view functions, are endpoints that only read data from the contract. To send a query to the observer nodes, we can proceed as follows: + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const query = new SmartContractQuery({ + contract: Address.newFromBech32("erd1qqqqqqqqqqqqqpgqqy34h7he2ya6qcagqre7ur7cc65vt0mxrc8qnudkr4"), + function: "getSum", + arguments: [], + }); + const response = await api.queryContract(query); + } + // ``` + + // ### Custom Api/Proxy calls + // The methods exposed by the `ApiNetworkProvider` or `ProxyNetworkProvider` are the most common and widely used. However, there may be times when custom API calls are needed. For these cases, we’ve created generic methods for both GET and POST requests. + // Let’s assume we want to retrieve all the transactions sent by Alice in which the `delegate` function was called. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const url = `transactions/${alice.toBech32()}?function=delegate`; + + const response = await api.doGetGeneric(url); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/relayed.js b/cookbook/relayed.js deleted file mode 100644 index fb1aa950..00000000 --- a/cookbook/relayed.js +++ /dev/null @@ -1,149 +0,0 @@ -import { Account, Address, DevnetEntrypoint, Transaction } from "@multiversx/sdk-core"; // md-ignore -import path from 'path'; // md-ignore -// ### Relayed transactions -// We are currently on the `third iteration (V3)` of relayed transactions. V1 and V2 will soon be deactivated, so we will focus on V3. - -// For V3, two new fields have been added to transactions: -// - relayer -// - relayerSignature - -// Signing Process: -// 1. The relayer must be set before the sender signs the transaction. -// 2. Once the sender has signed, the relayer can also sign the transaction and broadcast it. - -// **Important Consideration**: -// Relayed V3 transactions require an additional `50,000` gas. -// Let’s see how to create a relayed transaction: - -// ```js -{ - const walletsPath = path.join("src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); - const bob = await Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = new Transaction({ - chainID: "D", - sender: alice.address, - receiver: bob, - relayer: carol.address, - gasLimit: 110_000n, - data: Buffer.from("hello"), - nonce: alice.getNonceThenIncrement() - }); - - // sender signs the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // relayer signs the transaction // md-as-comment - transaction.RelayerSignature = await carol.signTransaction(transaction); - - // broadcast the transaction // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Creating relayed transactions using controllers -// We can create relayed transactions using any of the available controllers. -// Each controller includes a relayer argument, which must be set if we want to create a relayed transaction. - -// Let’s issue a fungible token using a relayed transaction: - -// ```js -{ - // create the entrypoint and the token management controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.creatTokenManagementController(); - - // create the issuer of the token // md-as-comment - const walletsPath = path.join("src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); - - // Carol will be our relayer, that means she is paying the gas for the transaction // md-as-comment - const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = await controller.createTransactionForIssuingFungible( - alice, - alice.getNonceThenIncrement(), - { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - guardian: carol.address, - }, - ); - - // relayer also signs the transaction // md-as-comment - transaction.relayerSignature = await carol.signTransaction(transaction); - - // broadcast the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// #### Creating relayed transactions using factories -// Unlike controllers, `transaction factories` do not have a `relayer` argument. Instead, the **relayer must be set after creating the transaction**. -// This approach is beneficial because the **transaction is not signed by the sender at the time of creation**, allowing flexibility in setting the relayer before signing. - -// Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: - -// ```js -{ - // create the entrypoint and the token management factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.creatTokenManagementController(); - - // create the issuer of the token // md-as-comment - const walletsPath = path.join("src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); - - // carol will be our relayer, that means she is paying the gas for the transaction // md-as-comment - const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); - - const transaction = await factory.createTransactionForIssuingFungible( - alice.address, - { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - }, - ); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // set the relayer // md-as-comment - transaction.relayer = carol.address; - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // relayer also signs the transaction // md-as-comment - transaction.relayerSignature = await carol.signTransaction(transaction); - - // broadcast the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` diff --git a/cookbook/relayed.ts b/cookbook/relayed.ts new file mode 100644 index 00000000..b85d44af --- /dev/null +++ b/cookbook/relayed.ts @@ -0,0 +1,146 @@ +import path from "path"; // md-ignore +import { Account, Address, DevnetEntrypoint, Transaction } from "../src"; // md-ignore +(async () => { + // ### Relayed transactions + // We are currently on the `third iteration (V3)` of relayed transactions. V1 and V2 will soon be deactivated, so we will focus on V3. + + // For V3, two new fields have been added to transactions: + // - relayer + // - relayerSignature + + // Signing Process: + // 1. The relayer must be set before the sender signs the transaction. + // 2. Once the sender has signed, the relayer can also sign the transaction and broadcast it. + + // **Important Consideration**: + // Relayed V3 transactions require an additional `50,000` gas. + // Let’s see how to create a relayed transaction: + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const walletsPath = path.join("../src", "testdata", "testwallets"); + const bob = await Account.newFromPem(path.join(walletsPath, "bob.pem")); + const grace = Address.newFromBech32("erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede"); + const mike = await Account.newFromPem(path.join(walletsPath, "mike.pem")); + + // fetch the nonce of the network // md-as-comment + bob.nonce = await entrypoint.recallAccountNonce(bob.address); + + const transaction = new Transaction({ + chainID: "D", + sender: bob.address, + receiver: grace, + relayer: mike.address, + gasLimit: 110_000n, + data: Buffer.from("hello"), + nonce: bob.getNonceThenIncrement(), + }); + + // sender signs the transaction // md-as-comment + transaction.signature = await bob.signTransaction(transaction); + + // relayer signs the transaction // md-as-comment + transaction.relayerSignature = await mike.signTransaction(transaction); + + // broadcast the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Creating relayed transactions using controllers + // We can create relayed transactions using any of the available controllers. + // Each controller includes a relayer argument, which must be set if we want to create a relayed transaction. + + // Let’s issue a fungible token using a relayed transaction: + + // ```js + { + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token // md-as-comment + const walletsPath = path.join("../src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // Carol will be our relayer, that means she is paying the gas for the transaction // md-as-comment + const frank = await Account.newFromPem(path.join(walletsPath, "frank.pem")); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingFungible(alice, alice.getNonceThenIncrement(), { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + relayer: frank.address, + }); + + // relayer also signs the transaction // md-as-comment + transaction.relayerSignature = await frank.signTransaction(transaction); + + // broadcast the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Creating relayed transactions using factories + // Unlike controllers, `transaction factories` do not have a `relayer` argument. Instead, the **relayer must be set after creating the transaction**. + // This approach is beneficial because the **transaction is not signed by the sender at the time of creation**, allowing flexibility in setting the relayer before signing. + + // Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: + + // ```js + { + // create the entrypoint and the token management factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token // md-as-comment + const walletsPath = path.join("../src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + + // carol will be our relayer, that means she is paying the gas for the transaction // md-as-comment + const frank = await Account.newFromPem(path.join(walletsPath, "frank.pem")); + + const transaction = await factory.createTransactionForIssuingFungible(alice.address, { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // set the relayer // md-as-comment + transaction.relayer = frank.address; + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // relayer also signs the transaction // md-as-comment + transaction.relayerSignature = await frank.signTransaction(transaction); + + // broadcast the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/signingObjects.js b/cookbook/signingObjects.js deleted file mode 100644 index 30c62a76..00000000 --- a/cookbook/signingObjects.js +++ /dev/null @@ -1,118 +0,0 @@ - -import { Account, Address, Message, MessageComputer, Transaction, TransactionComputer, UserSecretKey } from "@multiversx/sdk-core"; // md-ignore -import path from 'path'; // md-ignore -// ## Signing objects - -// Signing is done using an account's secret key. To simplify this process, we provide wrappers like [Account](#creating-accounts), which streamline signing operations. -// First, we'll explore how to sign using an Account, followed by signing directly with a secret key. - -// #### Signing a Transaction using an Account -// We are going to assume we have an account at this point. If you don't, feel free to check out the [creating an account](#creating-accounts) section. -// ```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const transaction = new Transaction({ - chainID: "D", - sender: alice.address, - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - gasLimit: 50000n, - nonce: 90n - }); - - transaction.signature = await alice.signTransaction(transaction); - console.log(transaction.toPlainObject()); -} -// ``` - -// #### Signing a Transaction using a SecretKey -// ```js -{ - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = UserSecretKey.fromString(secretKeyHex); - const publickKey = secretKey.generatePublicKey(); - - const transaction = new Transaction({ - nonce: 90n, - sender: publickKey.toAddress(), - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - value: 1000000000000000000n, - gasLimit: 50000n, - chainID: "D" - }); - - // serialize the transaction // md-as-comment - const transactionComputer = new TransactionComputer(); - const serializedTransaction = transactionComputer.computeBytesForSigning(transaction); - - // apply the signature on the transaction // md-as-comment - transaction.signature = await secretKey.sign(serializedTransaction); - - console.log(transaction.toPlainObject()); -} -// ``` - -// #### Signing a Transaction by hash -// ```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const transaction = new Transaction({ - nonce: 90n, - sender: publickKey.toAddress(), - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - value: 1000000000000000000n, - gasLimit: 50000n, - chainID: "D" - }); - - const transactionComputer = new TransactionComputer(); - - // sets the least significant bit of the options field to `1` // md-as-comment - transactionComputer.applyOptionsForHashSigning(transaction); - - // compute a keccak256 hash for signing // md-as-comment - const hash = transactionComputer.computeHashForSigning(transaction); - - // sign and apply the signature on the transaction // md-as-comment - transaction.signature = await alice.signTransaction(hash); - - console.log(transaction.toPlainObject()); -} -// ``` - -// #### Signing a Message using an Account: -// ```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const message = new Message({ - data: new Uint8Array(Buffer.from("hello")), - address: alice.address - }); - - message.signature = await alice.signMessage(message); -} -// ``` - -// #### Signing a Message using an SecretKey: -// ```js -{ - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = UserSecretKey.fromString(secretKeyHex); - const publicKey = secretKey.generatePublicKey(); - - const messageComputer = new MessageComputer(); - const message = new Message({ - data: new Uint8Array(Buffer.from("hello")), - address: publicKey.toAddress() - }); - // serialized the message - const serialized = messageComputer.computeBytesForSigning(message); - - message.signature = await secretKey.sign(serialized); -} -// ``` diff --git a/cookbook/signingObjects.ts b/cookbook/signingObjects.ts new file mode 100644 index 00000000..01974283 --- /dev/null +++ b/cookbook/signingObjects.ts @@ -0,0 +1,121 @@ +import path from "path"; // md-ignore +import { Account, Address, Message, MessageComputer, Transaction, TransactionComputer, UserSecretKey } from "../src"; // md-ignore +(async () => { + // ## Signing objects + + // Signing is done using an account's secret key. To simplify this process, we provide wrappers like [Account](#creating-accounts), which streamline signing operations. + // First, we'll explore how to sign using an Account, followed by signing directly with a secret key. + + // #### Signing a Transaction using an Account + // We are going to assume we have an account at this point. If you don't, feel free to check out the [creating an account](#creating-accounts) section. + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + chainID: "D", + sender: alice.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + gasLimit: 50000n, + nonce: 90n, + }); + + transaction.signature = await alice.signTransaction(transaction); + console.log(transaction.toPlainObject()); + } + // ``` + + // #### Signing a Transaction using a SecretKey + // ```js + { + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = UserSecretKey.fromString(secretKeyHex); + const publickKey = secretKey.generatePublicKey(); + + const transaction = new Transaction({ + nonce: 90n, + sender: publickKey.toAddress(), + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D", + }); + + // serialize the transaction // md-as-comment + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForSigning(transaction); + + // apply the signature on the transaction // md-as-comment + transaction.signature = await secretKey.sign(serializedTransaction); + + console.log(transaction.toPlainObject()); + } + // ``` + + // #### Signing a Transaction by hash + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + nonce: 90n, + sender: alice.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D", + }); + + const transactionComputer = new TransactionComputer(); + + // sets the least significant bit of the options field to `1` // md-as-comment + transactionComputer.applyOptionsForHashSigning(transaction); + + // compute a keccak256 hash for signing // md-as-comment + const hash = transactionComputer.computeHashForSigning(transaction); + + // sign and apply the signature on the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + console.log(transaction.toPlainObject()); + } + // ``` + + // #### Signing a Message using an Account: + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: alice.address, + }); + + message.signature = await alice.signMessage(message); + } + // ``` + + // #### Signing a Message using an SecretKey: + // ```js + { + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = UserSecretKey.fromString(secretKeyHex); + const publicKey = secretKey.generatePublicKey(); + + const messageComputer = new MessageComputer(); + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: publicKey.toAddress(), + }); + // serialized the message + const serialized = messageComputer.computeBytesForSigning(message); + + message.signature = await secretKey.sign(serialized); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/smartContracts.js b/cookbook/smartContracts.js deleted file mode 100644 index a37e256b..00000000 --- a/cookbook/smartContracts.js +++ /dev/null @@ -1,533 +0,0 @@ -import { AbiRegistry, Account, Address, AddressComputer, BigUIntValue, DevnetEntrypoint, Field, SmartContractTransactionsOutcomeParser, Struct, TokenIdentifierValue, U32Value, U64Value } from "@multiversx/sdk-core"; // md-ignore -import axios from "axios"; // md-ignore -import { promises } from "fs"; // md-ignore -import path from 'path'; // md-ignore -// ### Smart Contracts - -// #### Contract ABIs - -// A contract's ABI (Application Binary Interface) describes the endpoints, data structures, and events that the contract exposes. -// While interactions with the contract are possible without the ABI, they are much easier to implement when the definitions are available. - -// #### Loading the ABI from a file -// ```js -{ - let abiJson = await promises.readFile("../contracts/adder.abi.json", { encoding: "utf8" }); - let abiObj = JSON.parse(abiJson); - let abi = AbiRegistry.create(abiObj); -} -// ``` - -// #### Loading the ABI from an URL - -// ```js -{ - const response = await axios.get("https://github.com/multiversx/mx-sdk-js-core/raw/main/src/testdata/adder.abi.json"); - abi = AbiRegistry.create(response.data); -} -// ``` - -// #### Manually construct the ABI - -// If an ABI file isn’t available, but you know the contract’s endpoints and data types, you can manually construct the ABI. - -// ```js -{ - abi = AbiRegistry.create({ - "endpoints": [{ - "name": "add", - "inputs": [], - "outputs": [] - }] - }); -} -// ``` - -// ```js -{ - abi = AbiRegistry.create({ - "endpoints": [ - { - "name": "foo", - "inputs": [ - { "type": "BigUint" }, - { "type": "u32" }, - { "type": "Address" } - ], - "outputs": [ - { "type": "u32" } - ] - }, - { - "name": "bar", - "inputs": [ - { "type": "counted-variadic" }, - { "type": "variadic" } - ], - "outputs": [] - } - ] - }); -} -// ``` - -// ### Smart Contract deployments -// For creating smart contract deployment transactions, we have two options: a controller and a factory. Both function similarly to the ones used for token transfers. -// When creating transactions that interact with smart contracts, it's recommended to provide the ABI file to the controller or factory if possible. -// This allows arguments to be passed as native Javascript values. If the ABI is not available, but we know the expected data types, we can pass arguments as typed values (e.g., `BigUIntValue`, `ListValue`, `StructValue`, etc.) or as raw bytes. - -// #### Deploying a Smart Contract Using the Controller - -// ```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const sender = await Account.newFromPem(filePath); - const entrypoint = new DevnetEntrypoint(); - - // the developer is responsible for managing the nonce - sender.nonce = await entrypoint.recallAccountNonce(sender.address); - - // load the contract bytecode - const bytecode = await promises.readFile("../contracts/adder.wasm"); - // load the abi file - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); - - const controller = entrypoint.createSmartContractController(abi); - - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment - let args = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment - args = [42]; - - const deployTransaction = await controller.createTransactionForDeploy( - sender, - sender.getNonceThenIncrement(), - { - bytecode: bytecode, - gasLimit: 6000000n, - arguments: args, - }, - ); - - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(deployTransaction); -} -// ``` - -// md-insert:mixedTypedValuesAndNativeValues - -// #### Parsing contract deployment transactions - -// ```js -{ - // We use the transaction hash we got when broadcasting the transaction - const outcome = await controller.awaitCompletedDeploy(txHash); // waits for transaction completion and parses the result - const contractAddress = outcome.contracts[0].address; -} -// ``` - -// If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: - -// ```js -{ - // We use the transaction hash we got when broadcasting the transaction - // If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: - const transactionOnNetwork = await controller.awaitTransactionCompleted(txHash); - - // parsing the transaction - const outcome = await controller.parseDeploy(transactionOnNetwork); -} -// ``` - -// #### Computing the contract address - -// Even before broadcasting, at the moment you know the sender's address and the nonce for your deployment transaction, you can (deterministically) compute the (upcoming) address of the smart contract: - -// ```js -{ - const addressComputer = new AddressComputer(); - const contractAddress = addressComputer.computeContractAddress( - deployTransaction.sender, - deployTransaction.nonce - ); - - console.log("Contract address:", contractAddress.toBech32()); -} -// ``` - -// #### Deploying a Smart Contract using the factory -// After the transaction is created the nonce needs to be properly set and the transaction should be signed before broadcasting it. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTransfersTransactionsFactory(); - - // load the contract bytecode - const bytecode = await promises.readFile("../contracts/adder.wasm"); - - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment - let args = [new BigUIntValue(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment - args = [42]; - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const deployTransaction = await factory.createTransactionForDeploy( - sender, - { - bytecode: bytecode, - gasLimit: 6000000n, - arguments: args, - }, - ); - - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(sender.address); - - // set the nonce - deployTransaction.nonce = alice.nonce; - - // sign the transaction - deployTransaction.signature = await alice.signTransaction(transaction); - - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(deployTransaction); - - // waiting for transaction to complete - const transactionOnNetwork = await entrypoint.awaitTransactionCompleted(txHash); - - // parsing transaction - const parser = new SmartContractTransactionsOutcomeParser(); - const parsedOutcome = parser.parseDeploy(transactionOnNetwork); - const contractAddress = parsedOutcome.contracts[0].address; - - console.log(contractAddress.toBech32()); -} -// ``` - -// ### Smart Contract calls - -// In this section we'll see how we can call an endpoint of our previously deployed smart contract using both approaches with the `controller` and the `factory`. - -// #### Calling a smart contract using the controller - -// ```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const sender = await Account.newFromPem(filePath); - const entrypoint = new DevnetEntrypoint(); - - // the developer is responsible for managing the nonce - sender.nonce = await entrypoint.recallAccountNonce(sender.address); - - // load the abi file - const abi = await loadAbiRegistry("src/testdata/adder.abi.json");; - const controller = entrypoint.createSmartContractController(abi); - - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment - let args = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment - args = [42]; - - const transaction = await controller.createTransactionForExecute( - sender, - sender.getNonceThenIncrement(), - { - contract: contractAddress, - gasLimit: 5000000n, - function: "add", - arguments: args, - }, - ); - - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(transaction); - - console.log(txHash); -} -// ``` - -// #### Parsing smart contract call transactions -// In our case, calling the add endpoint does not return anything, but similar to the example above, we could parse this transaction to get the output values of a smart contract call. - -// ```js -{ - // waits for transaction completion and parses the result - const parsedOutcome = controller.awaitCompletedExecute(transactionOnNetwork); - const values = parsedOutcome.contracts.values; -} -// ``` - -// #### Calling a smart contract and sending tokens (transfer & execute) -// Additionally, if an endpoint requires a payment when called, we can send tokens to the contract while creating a smart contract call transaction. -// Both EGLD and ESDT tokens or a combination of both can be sent. This functionality is supported by both the controller and the factory. - -// ```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const sender = await Account.newFromPem(filePath); - const entrypoint = new DevnetEntrypoint(); - - // the developer is responsible for managing the nonce - sender.nonce = await entrypoint.recallAccountNonce(sender.address); - - // load the abi file - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); - - // get the smart contracts controller - const controller = entrypoint.createSmartContractController(abi); - - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment - let args = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment - args = [42]; - - // creating the transfers - const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10 }); - const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); - - const secondToken = new Token({ identifier: "BAR-c80d29" }); - const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); - - const transaction = await controller.createTransactionForExecute( - sender, - sender.getNonceThenIncrement(), - { - contract: contractAddress, - gasLimit: 5000000n, - function: "add", - arguments: args, - nativeTransferAmount: 1000000000000000000n, - tokenTransfers: [firstTransfer, secondTransfer] - }, - ); - - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(transaction); - - console.log(txHash); -} -// ``` - -// #### Calling a smart contract using the factory -// Let's create the same smart contract call transaction, but using the `factory`. - -// ```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const entrypoint = new DevnetEntrypoint(); - - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - // load the abi file - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); - - // get the smart contracts controller - const controller = entrypoint.createSmartContractTransactionsFactory(abi); - - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment - let args = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment - args = [42]; - - // creating the transfers - const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10 }); - const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); - - const secondToken = new Token({ identifier: "BAR-c80d29" }); - const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); - - const transaction = await controller.createTransactionForExecute( - sender, - { - contract: contractAddress, - gasLimit: 5000000n, - function: "add", - arguments: args, - nativeTransferAmount: 1000000000000000000n, - tokenTransfers: [firstTransfer, secondTransfer] - }, - ); - - transaction.nonce = alice.getNonceThenIncrement(); - transaction.signature = await alice.signTransaction(transaction); - - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(transaction); - - console.log(txHash); -} -// ``` - -// #### Parsing transaction outcome -// As said before, the `add` endpoint we called does not return anything, but we could parse the outcome of smart contract call transactions, as follows: - -// ```js -{ - // load the abi file - const entrypoint = new DevnetEntrypoint(); - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); - const parser = SmartContractTransactionsOutcomeParser({ abi }); - const transactionOnNetwork = entrypoint.getTransaction(txHash); - const outcome = parser.parseExecute(transactionOnNetwork); -} -// ``` - -// #### Decoding transaction events -// You might be interested into decoding events emitted by a contract. You can do so by using the `TransactionEventsParser`. - -// Suppose we'd like to decode a `startPerformAction` event emitted by the [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract. - -// First, we load the abi file, then we fetch the transaction, we extract the event from the transaction and then we parse it. - -// ```js -{ - // load the abi files - const entrypoint = new DevnetEntrypoint(); - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); - const parser = new TransactionEventsParser({ abi }); - const transactionOnNetwork = entrypoint.getTransaction(txHash); - const events = gatherAllEvents(transactionOnNetwork); - const outcome = parser.parseEvents({ events }); -} -// ``` - -// #### Decoding transaction events -// Whenever needed, the contract ABI can be used for manually encoding or decoding custom types. - -// Let's encode a struct called EsdtTokenPayment (of [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract) into binary data. -// ```js -{ - const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); - const paymentType = abi.getStruct("EsdtTokenPayment"); - - const paymentStruct = new Struct(paymentType, [ - new Field(new TokenIdentifierValue("TEST-8b028f"), "token_identifier"), - new Field(new U64Value(0n), "token_nonce"), - new Field(new BigUIntValue(10000n), "amount") - ]); - - const encoded = codec.encodeNested(paymentStruct); - - console.log(encoded.toString("hex")); -} -// ``` - -// Now let's decode a struct using the ABI. -// ```js -{ - const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); - const actionStructType = abi.getEnum("Action"); - const data = Buffer.from("0500000000000000000500d006f73c4221216fa679bc559005584c4f1160e569e1000000012a0000000003616464000000010000000107", "hex"); - - const [decoded] = codec.decodeNested(data, actionStructType); - const decodedValue = decoded.valueOf(); - console.log(JSON.stringify(decodedValue, null, 4)); -} -// ``` - -// ### Smart Contract queries -// When querying a smart contract, a **view function** is called. A view function does not modify the state of the contract, so we do not need to send a transaction. -// To perform this query, we use the **SmartContractController**. While we can use the contract's ABI file to encode the query arguments, we can also use it to parse the result. -// In this example, we will query the **adder smart contract** by calling its `getSum` endpoint. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); - - // create the controller - const controller = entrypoint.createSmartContractController(abi); - - // creates the query, runs the query, parses the result - const response = await controller.query({ contract: contractAddress, function: "getSum", arguments: [] }); -} -// ``` - -// If we need more granular control, we can split the process into three steps: **create the query, run the query, and parse the query response**. -// This approach achieves the same result as the previous example. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - - // load the abi - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); - - // the contract address we'll query - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - - // create the controller - const controller = entrypoint.createSmartContractController(abi); - - // create the query - const query = await controller.createQuery({ contract: contractAddress, function: "getSum", arguments: [] }); - // runs the query - const response = await controller.runQuery(query); - - // parse the result - const parsedResponse = controller.parseQueryResponse(response); -} -// ``` - -// ### Upgrading a smart contract -// Contract upgrade transactions are similar to deployment transactions (see above) because they also require contract bytecode. -// However, in this case, the contract address is already known. Like deploying a smart contract, we can upgrade a smart contract using either the **controller** or the **factory**. - -// #### Uprgrading a smart contract using the controller -// ```js -{ - // prepare the account - const entrypoint = new DevnetEntrypoint(); - const keystorePath = path.join("src", "testdata", "testwallets", "alice.json"); - const account = Account.newFromKeystore({ - filePath: keystorePath, - password: "password" - }); - // the developer is responsible for managing the nonce - account.nonce = entrypoint.recall_account_nonce(account.address); - - // load the abi - const abi = await loadAbiRegistry("src/testdata/adder.abi.json"); - - // create the controller - const controller = entrypoint.createSmartContractController(abi); - - // load the contract bytecode; this is the new contract code, the one we want to upgrade to - const bytecode = await promises.readFile("../contracts/adder.wasm"); - - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment - let args = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment - args = [42]; - - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - - const upgradeTransaction = await controller.createTransactionForUpgrade( - sender, - sender.getNonceThenIncrement(), - { - contract: contractAddress, - bytecode: bytecode, - gasLimit: 6000000n, - arguments: args, - }, - ); - - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(upgradeTransaction); - - console.log({ txHash }); -} -// ``` \ No newline at end of file diff --git a/cookbook/smartContracts.ts b/cookbook/smartContracts.ts new file mode 100644 index 00000000..fa6bc2ed --- /dev/null +++ b/cookbook/smartContracts.ts @@ -0,0 +1,570 @@ +import axios from "axios"; // md-ignore +import { promises } from "fs"; // md-ignore +import path from "path"; // md-ignore +import { + Abi, + Account, + Address, + AddressComputer, + BigUIntValue, + BinaryCodec, + DevnetEntrypoint, + Field, + gatherAllEvents, + SmartContractTransactionsOutcomeParser, + Struct, + Token, + TokenIdentifierValue, + TokenTransfer, + TransactionEventsParser, + U32Value, + U64Value, +} from "../src"; // md-ignore +import { loadAbiRegistry } from "../src/testutils"; +(async () => { + // ### Smart Contracts + + // #### Contract ABIs + + // A contract's ABI (Application Binary Interface) describes the endpoints, data structures, and events that the contract exposes. + // While interactions with the contract are possible without the ABI, they are much easier to implement when the definitions are available. + + // #### Loading the ABI from a file + // ```js + { + let abiJson = await promises.readFile("../src/testData/adder.abi.json", { encoding: "utf8" }); + let abiObj = JSON.parse(abiJson); + let abi = Abi.create(abiObj); + } + // ``` + + // #### Loading the ABI from an URL + + // ```js + { + const response = await axios.get( + "https://github.com/multiversx/mx-sdk-js-core/raw/main/src/testdata/adder.abi.json", + ); + let abi = Abi.create(response.data); + } + // ``` + + // #### Manually construct the ABI + + // If an ABI file isn’t available, but you know the contract’s endpoints and data types, you can manually construct the ABI. + + // ```js + { + let abi = Abi.create({ + endpoints: [ + { + name: "add", + inputs: [], + outputs: [], + }, + ], + }); + } + // ``` + + // ```js + { + let abi = Abi.create({ + endpoints: [ + { + name: "foo", + inputs: [{ type: "BigUint" }, { type: "u32" }, { type: "Address" }], + outputs: [{ type: "u32" }], + }, + { + name: "bar", + inputs: [{ type: "counted-variadic" }, { type: "variadic" }], + outputs: [], + }, + ], + }); + } + // ``` + + // ### Smart Contract deployments + // For creating smart contract deployment transactions, we have two options: a controller and a factory. Both function similarly to the ones used for token transfers. + // When creating transactions that interact with smart contracts, it's recommended to provide the ABI file to the controller or factory if possible. + // This allows arguments to be passed as native Javascript values. If the ABI is not available, but we know the expected data types, we can pass arguments as typed values (e.g., `BigUIntValue`, `ListValue`, `StructValue`, etc.) or as raw bytes. + + // #### Deploying a Smart Contract Using the Controller + + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); + + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); + + // load the contract bytecode + const bytecode = await promises.readFile("../src/testData/adder.wasm"); + // load the abi file + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + + const controller = entrypoint.createSmartContractController(abi); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment + args = [42]; + + const deployTransaction = await controller.createTransactionForDeploy(sender, sender.getNonceThenIncrement(), { + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(deployTransaction); + } + // ``` + + // :::tip + // When creating transactions using `class:SmartContractController` or `class:SmartContractTransactionsFactory`, even if the ABI is available and provided, + // you can still use `class:TypedValue` objects as arguments for deployments and interactions. + + // Even further, you can use a mix of `class:TypedValue` objects and plain JavaScript values and objects. For example: + + // ```js + // let args = [new U32Value(42), "hello", { foo: "bar" }, new TokenIdentifierValue("TEST-abcdef")]; + // ``` + // ::: + + // #### Parsing contract deployment transactions + + // ```js + { + // We use the transaction hash we got when broadcasting the transaction + + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createSmartContractController(abi); + const outcome = await controller.awaitCompletedDeploy("txHash"); // waits for transaction completion and parses the result + const contractAddress = outcome.contracts[0].address; + } + // ``` + + // If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: + + // ```js + { + // We use the transaction hash we got when broadcasting the transaction + // If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: + + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createSmartContractController(); + const networkProvider = entrypoint.createNetworkProvider(); + const transactionOnNetwork = await networkProvider.awaitTransactionCompleted("txHash"); + + // parsing the transaction + const outcome = await controller.parseDeploy(transactionOnNetwork); + } + // ``` + + // #### Computing the contract address + + // Even before broadcasting, at the moment you know the sender's address and the nonce for your deployment transaction, you can (deterministically) compute the (upcoming) address of the smart contract: + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createSmartContractTransactionsFactory(); + const bytecode = await promises.readFile("../contracts/adder.wasm"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment + let args: any[] = [new BigUIntValue(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment + args = [42]; + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const deployTransaction = factory.createTransactionForDeploy(alice.address, { + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }); + const addressComputer = new AddressComputer(); + const contractAddress = addressComputer.computeContractAddress( + deployTransaction.sender, + deployTransaction.nonce, + ); + + console.log("Contract address:", contractAddress.toBech32()); + } + // ``` + + // #### Deploying a Smart Contract using the factory + // After the transaction is created the nonce needs to be properly set and the transaction should be signed before broadcasting it. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createSmartContractTransactionsFactory(); + + // load the contract bytecode + const bytecode = await promises.readFile("../src/testData/adder.wasm"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment + let args: any[] = [new BigUIntValue(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment + args = [42]; + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const deployTransaction = await factory.createTransactionForDeploy(alice.address, { + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // set the nonce + deployTransaction.nonce = alice.nonce; + + // sign the transaction + deployTransaction.signature = await alice.signTransaction(deployTransaction); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(deployTransaction); + + // waiting for transaction to complete + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // parsing transaction + const parser = new SmartContractTransactionsOutcomeParser(); + const parsedOutcome = parser.parseDeploy({ transactionOnNetwork }); + const contractAddress = parsedOutcome.contracts[0].address; + + console.log(contractAddress.toBech32()); + } + // ``` + + // ### Smart Contract calls + + // In this section we'll see how we can call an endpoint of our previously deployed smart contract using both approaches with the `controller` and the `factory`. + + // #### Calling a smart contract using the controller + + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); + + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); + + // load the abi file + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + const controller = entrypoint.createSmartContractController(abi); + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment + args = [42]; + + const transaction = await controller.createTransactionForExecute(sender, sender.getNonceThenIncrement(), { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + }); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + console.log(txHash); + } + // ``` + + // #### Parsing smart contract call transactions + // In our case, calling the add endpoint does not return anything, but similar to the example above, we could parse this transaction to get the output values of a smart contract call. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createSmartContractController(); + const txHash = "b3ae88ad05c464a74db73f4013de05abcfcb4fb6647c67a262a6cfdf330ef4a9"; + // waits for transaction completion and parses the result + const parsedOutcome = await controller.awaitCompletedExecute(txHash); + const values = parsedOutcome.values; + } + // ``` + + // #### Calling a smart contract and sending tokens (transfer & execute) + // Additionally, if an endpoint requires a payment when called, we can send tokens to the contract while creating a smart contract call transaction. + // Both EGLD and ESDT tokens or a combination of both can be sent. This functionality is supported by both the controller and the factory. + + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); + + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); + + // load the abi file + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + + // get the smart contracts controller + const controller = entrypoint.createSmartContractController(abi); + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment + args = [42]; + + // creating the transfers + const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10n }); + const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); + + const secondToken = new Token({ identifier: "BAR-c80d29" }); + const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); + + const transaction = await controller.createTransactionForExecute(sender, sender.getNonceThenIncrement(), { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + nativeTransferAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer], + }); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + console.log(txHash); + } + // ``` + + // #### Calling a smart contract using the factory + // Let's create the same smart contract call transaction, but using the `factory`. + + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + // get the smart contracts controller + const controller = entrypoint.createSmartContractTransactionsFactory(); + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment + args = [42]; + + // creating the transfers + const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10n }); + const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); + + const secondToken = new Token({ identifier: "BAR-c80d29" }); + const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); + + const transaction = await controller.createTransactionForExecute(alice.address, { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + nativeTransferAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer], + }); + + transaction.nonce = alice.getNonceThenIncrement(); + transaction.signature = await alice.signTransaction(transaction); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + console.log(txHash); + } + // ``` + + // #### Parsing transaction outcome + // As said before, the `add` endpoint we called does not return anything, but we could parse the outcome of smart contract call transactions, as follows: + + // ```js + { + // load the abi file + const entrypoint = new DevnetEntrypoint(); + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + const parser = new SmartContractTransactionsOutcomeParser({ abi }); + const txHash = "b3ae88ad05c464a74db73f4013de05abcfcb4fb6647c67a262a6cfdf330ef4a9"; + const transactionOnNetwork = await entrypoint.getTransaction(txHash); + const outcome = parser.parseExecute({ transactionOnNetwork }); + } + // ``` + + // #### Decoding transaction events + // You might be interested into decoding events emitted by a contract. You can do so by using the `TransactionEventsParser`. + + // Suppose we'd like to decode a `startPerformAction` event emitted by the [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract. + + // First, we load the abi file, then we fetch the transaction, we extract the event from the transaction and then we parse it. + + // ```js + { + // load the abi files + const entrypoint = new DevnetEntrypoint(); + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + const parser = new TransactionEventsParser({ abi }); + const txHash = "b3ae88ad05c464a74db73f4013de05abcfcb4fb6647c67a262a6cfdf330ef4a9"; + const transactionOnNetwork = await entrypoint.getTransaction(txHash); + const events = gatherAllEvents(transactionOnNetwork); + const outcome = parser.parseEvents({ events }); + } + // ``` + + // #### Decoding transaction events + // Whenever needed, the contract ABI can be used for manually encoding or decoding custom types. + + // Let's encode a struct called EsdtTokenPayment (of [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract) into binary data. + // ```js + { + const abi = await loadAbiRegistry("../src/testdata/multisig-full.abi.json"); + const paymentType = abi.getStruct("EsdtTokenPayment"); + const codec = new BinaryCodec(); + + const paymentStruct = new Struct(paymentType, [ + new Field(new TokenIdentifierValue("TEST-8b028f"), "token_identifier"), + new Field(new U64Value(0n), "token_nonce"), + new Field(new BigUIntValue(10000n), "amount"), + ]); + + const encoded = codec.encodeNested(paymentStruct); + + console.log(encoded.toString("hex")); + } + // ``` + + // Now let's decode a struct using the ABI. + // ```js + { + const abi = await loadAbiRegistry("../src/testdata/multisig-full.abi.json"); + const actionStructType = abi.getEnum("Action"); + const data = Buffer.from( + "0500000000000000000500d006f73c4221216fa679bc559005584c4f1160e569e1000000012a0000000003616464000000010000000107", + "hex", + ); + + const codec = new BinaryCodec(); + const [decoded] = codec.decodeNested(data, actionStructType); + const decodedValue = decoded.valueOf(); + console.log(JSON.stringify(decodedValue, null, 4)); + } + // ``` + + // ### Smart Contract queries + // When querying a smart contract, a **view function** is called. A view function does not modify the state of the contract, so we do not need to send a transaction. + // To perform this query, we use the **SmartContractController**. While we can use the contract's ABI file to encode the query arguments, we can also use it to parse the result. + // In this example, we will query the **adder smart contract** by calling its `getSum` endpoint. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + + // create the controller + const controller = entrypoint.createSmartContractController(abi); + + // creates the query, runs the query, parses the result + const response = await controller.query({ contract: contractAddress, function: "getSum", arguments: [] }); + } + // ``` + + // If we need more granular control, we can split the process into three steps: **create the query, run the query, and parse the query response**. + // This approach achieves the same result as the previous example. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + + // load the abi + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + + // the contract address we'll query + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + // create the controller + const controller = entrypoint.createSmartContractController(abi); + + // create the query + const query = await controller.createQuery({ contract: contractAddress, function: "getSum", arguments: [] }); + // runs the query + const response = await controller.runQuery(query); + + // parse the result + const parsedResponse = controller.parseQueryResponse(response); + } + // ``` + + // ### Upgrading a smart contract + // Contract upgrade transactions are similar to deployment transactions (see above) because they also require contract bytecode. + // However, in this case, the contract address is already known. Like deploying a smart contract, we can upgrade a smart contract using either the **controller** or the **factory**. + + // #### Uprgrading a smart contract using the controller + // ```js + { + // prepare the account + const entrypoint = new DevnetEntrypoint(); + const keystorePath = path.join("../src", "testdata", "testwallets", "alice.json"); + const sender = Account.newFromKeystore(keystorePath, "password"); + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); + + // load the abi + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + + // create the controller + const controller = entrypoint.createSmartContractController(abi); + + // load the contract bytecode; this is the new contract code, the one we want to upgrade to + const bytecode = await promises.readFile("../src/testData/adder.wasm"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: // md-as-comment + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: // md-as-comment + args = [42]; + + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + + const upgradeTransaction = await controller.createTransactionForUpgrade( + sender, + sender.getNonceThenIncrement(), + { + contract: contractAddress, + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }, + ); + + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(upgradeTransaction); + + console.log({ txHash }); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/tokens.js b/cookbook/tokens.js deleted file mode 100644 index 776ee587..00000000 --- a/cookbook/tokens.js +++ /dev/null @@ -1,417 +0,0 @@ -import { Account, Address, DevnetEntrypoint, TokenManagementTransactionsOutcomeParser } from "@multiversx/sdk-core"; // md-ignore -import path from 'path'; // md-ignore -// ### Token management - -// In this section, we're going to create transactions to issue fungible tokens, issue semi-fungible tokens, create NFTs, set token roles, but also parse these transactions to extract their outcome (e.g. get the token identifier of the newly issued token). - -// These methods are available through the `TokenManagementController` and the `TokenManagementTransactionsFactory`. -// The controller also includes built-in methods for awaiting the completion of transactions and parsing their outcomes. -// For the factory, the same functionality can be achieved using the `TokenManagementTransactionsOutcomeParser`. - -// For scripts or quick network interactions, we recommend using the controller. However, for a more granular approach (e.g., DApps), the factory is the better choice. - -// #### Issuing fungible tokens using the controller -// ```js -{ - // create the entrypoint and the token management controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createTokenManagementController(); - - // create the issuer of the token // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = await controller.createTransactionForIssuingFungible( - alice, - alice.getNonceThenIncrement(), - { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - }, - ); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier // md-as-comment - const outcome = await entrypoint.awaitCompletedIssueFungible(txHash); - - const tokenIdentifier = outcome[0].tokenIdentifier; - -} -// ``` - -// #### Issuing fungible tokens using the factory -// ```js -{ - // create the entrypoint and the token management transactions factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); - - // create the issuer of the token // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const transaction = await factory.createTransactionForIssuingFungible( - alice, - { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - }, - ); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier // md-as-comment - // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` // md-as-comment - const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - - // extract the token identifier // md-as-comment - const parser = new TokenManagementTransactionsOutcomeParser(); - const outcome = parser.parseIssueFungible(transactionOnNetwork); - const tokenIdentifier = outcome[0].tokenIdentifier; -} -// ``` - - -// #### Setting special roles for fungible tokens using the controller -// ```js -{ - // create the entrypoint and the token management controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createTokenManagementController(); - - // create the issuer of the token // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - const transaction = await controller.createTransactionForSettingSpecialRoleOnFungibleToken( - alice, - alice.getNonceThenIncrement(), - { - user: bob, - tokenIdentifier: "TEST-123456", - addRoleLocalMint: true, - addRoleLocalBurn: true, - addRoleESDTTransferRole: true, - }, - ); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier // md-as-comment - const outcome = await entrypoint.awaitCompletedSetSpecialRoleOnFungibleToken(transaction); - - const roles = outcome[0].roles; - const user = outcome[0].userAddress; -} -// ``` - -// #### Setting special roles for fungible tokens using the factory -// ```js -{ - // create the entrypoint and the token management controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); - - // create the issuer of the token // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - const transaction = await factory.createTransactionForIssuingFungible( - alice, - { - user: bob, - tokenIdentifier: "TEST-123456", - addRoleLocalMint: true, - addRoleLocalBurn: true, - addRoleESDTTransferRole: true, - }, - ); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier // md-as-comment - // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` // md-as-comment - const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - - const parser = new TokenManagementTransactionsOutcomeParser(); - const outcome = parser.parseSetSpecialRole(transactionOnNetwork); - - const roles = outcome[0].roles; - const user = outcome[0].userAddress; -} -// ``` - -// #### Issuing semi-fungible tokens using the controller -// ```js -{ - // create the entrypoint and the token management controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createTokenManagementController(); - - // create the issuer of the token // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = await controller.createTransactionForIssuingSemiFungible( - alice, - alice.getNonceThenIncrement(), - { - tokenName: "NEWSEMI", - tokenTicker: "SEMI", - canFreeze: false, - canWipe: true, - canPause: false, - canTransferNFTCreateRole: true, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: true, - }, - ); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier // md-as-comment - const outcome = await entrypoint.awaitCompletedIssueSemiFungible(txHash); - - const tokenIdentifier = outcome[0].tokenIdentifier; -} -// ``` - -// #### Issuing semi-fungible tokens using the factory -// ```js -{ - // create the entrypoint and the token management controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); - - // create the issuer of the token // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const transaction = await factory.createTransactionForIssuingSemiFungible( - alice, - { - tokenName: "NEWSEMI", - tokenTicker: "SEMI", - canFreeze: false, - canWipe: true, - canPause: false, - canTransferNFTCreateRole: true, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: true, - }, - ); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - const txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier // md-as-comment - const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - - // extract the token identifier // md-as-comment - const parser = new TokenManagementTransactionsOutcomeParser(); - const outcome = parser.parseIssueSemiFungible(transactionOnNetwork); - - const tokenIdentifier = outcome[0].tokenIdentifier; -} -// ``` - -// #### Issuing NFT collection & creating NFTs using the controller - -// ```js -{ - // create the entrypoint and the token management controller // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.creatTokenManagementController(); - - // create the issuer of the token // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - let transaction = await controller.createTransactionForIssuingNonFungible( - alice, - alice.getNonceThenIncrement(), - { - tokenName: "NEWNFT", - tokenTicker: "NFT", - canFreeze: false, - canWipe: true, - canPause: false, - canTransferNFTCreateRole: true, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: true, - }, - ); - - // sending the transaction // md-as-comment - let txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier // md-as-comment - let outcome = await entrypoint.awaitCompletedIssueNonFungible(txHash); - - const collectionIdentifier = outcome[0].tokenIdentifier; - - // create an NFT // md-as-comment - transaction = controller.createTransactionForCreatingNft(alice, - alice.getNonceThenIncrement(), - { - tokenIdentifier: "FRANK-aa9e8d", - initialQuantity: 1n, - name: "test", - royalties: 1000, - hash: "abba", - attributes: Buffer.from("test"), - uris: ["a", "b"], - }, - ); - - // sending the transaction // md-as-comment - txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier // md-as-comment - outcome = await entrypoint.awaitCompletedCreateNft(txHash); - - const identifier = outcome[0].tokenIdentifier; - const nonce = outcome[0].nonce; - const initialQuantity = outcome[0].initialQuantity; - -} -// ``` - -// #### Issuing NFT collection & creating NFTs using the factory -// ```js -{ - // create the entrypoint and the token management transdactions factory // md-as-comment - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); - - // create the issuer of the token // md-as-comment - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - let transaction = await factory.createTransactionForIssuingNonFungible( - alice, - { - tokenName: "NEWNFT", - tokenTicker: "NFT", - canFreeze: false, - canWipe: true, - canPause: false, - canTransferNFTCreateRole: true, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: true, - }, - ); - // fetch the nonce of the network // md-as-comment - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - let txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier // md-as-comment - let transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - - // extract the token identifier // md-as-comment - let parser = new TokenManagementTransactionsOutcomeParser(); - let outcome = parser.parseIssueNonFungible(transactionOnNetwork); - - const collectionIdentifier = outcome[0].tokenIdentifier; - - transaction = await factory.createTransactionForCreatingNFT( - alice, - { - tokenIdentifier: "FRANK-aa9e8d", - initialQuantity: 1n, - name: "test", - royalties: 1000, - hash: "abba", - attributes: Buffer.from("test"), - uris: ["a", "b"], - }, - ); - - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction // md-as-comment - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction // md-as-comment - txHash = await entrypoint.sendTransaction(transaction); - - // ### wait for transaction to execute, extract the token identifier // md-as-comment - transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - - outcome = parser.parseIssueNonFungible(transactionOnNetwork); - - const identifier = outcome[0].tokenIdentifier; - const nonce = outcome[0].nonce; - const initialQuantity = outcome[0].initialQuantity; -} -// ``` - -// These are just a few examples of what you can do using the token management controller or factory. For a complete list of supported methods, please refer to the autogenerated documentation: - -// - [TokenManagementController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementController.html) -// - [TokenManagementTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementTransactionsFactory.html) \ No newline at end of file diff --git a/cookbook/tokens.ts b/cookbook/tokens.ts new file mode 100644 index 00000000..37bb1f5a --- /dev/null +++ b/cookbook/tokens.ts @@ -0,0 +1,396 @@ +import path from "path"; // md-ignore +import { Account, Address, DevnetEntrypoint, TokenManagementTransactionsOutcomeParser } from "../src"; // md-ignore +(async () => { + // ### Token management + + // In this section, we're going to create transactions to issue fungible tokens, issue semi-fungible tokens, create NFTs, set token roles, but also parse these transactions to extract their outcome (e.g. get the token identifier of the newly issued token). + + // These methods are available through the `TokenManagementController` and the `TokenManagementTransactionsFactory`. + // The controller also includes built-in methods for awaiting the completion of transactions and parsing their outcomes. + // For the factory, the same functionality can be achieved using the `TokenManagementTransactionsOutcomeParser`. + + // For scripts or quick network interactions, we recommend using the controller. However, for a more granular approach (e.g., DApps), the factory is the better choice. + + // #### Issuing fungible tokens using the controller + // ```js + { + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingFungible(alice, alice.getNonceThenIncrement(), { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + const outcome = await controller.awaitCompletedIssueFungible(txHash); + + const tokenIdentifier = outcome[0].tokenIdentifier; + } + // ``` + + // #### Issuing fungible tokens using the factory + // ```js + { + // create the entrypoint and the token management transactions factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForIssuingFungible(alice.address, { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` // md-as-comment + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier // md-as-comment + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueFungible(transactionOnNetwork); + const tokenIdentifier = outcome[0].tokenIdentifier; + } + // ``` + + // #### Setting special roles for fungible tokens using the controller + // ```js + { + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = await controller.createTransactionForSettingSpecialRoleOnFungibleToken( + alice, + alice.getNonceThenIncrement(), + { + user: bob, + tokenIdentifier: "TEST-123456", + addRoleLocalMint: true, + addRoleLocalBurn: true, + addRoleESDTTransferRole: true, + }, + ); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + const outcome = await controller.awaitCompletedSetSpecialRoleOnFungibleToken(txHash); + + const roles = outcome[0].roles; + const user = outcome[0].userAddress; + } + // ``` + + // #### Setting special roles for fungible tokens using the factory + // ```js + { + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForIssuingFungible(alice.address, { + tokenName: "TEST", + tokenTicker: "TEST", + initialSupply: 100n, + numDecimals: 0n, + canFreeze: true, + canWipe: true, + canPause: true, + canChangeOwner: true, + canUpgrade: false, + canAddSpecialRoles: false, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` // md-as-comment + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseSetSpecialRole(transactionOnNetwork); + + const roles = outcome[0].roles; + const user = outcome[0].userAddress; + } + // ``` + + // #### Issuing semi-fungible tokens using the controller + // ```js + { + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transaction = await controller.createTransactionForIssuingSemiFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWSEMI", + tokenTicker: "SEMI", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }, + ); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + const outcome = await controller.awaitCompletedIssueSemiFungible(txHash); + + const tokenIdentifier = outcome[0].tokenIdentifier; + } + // ``` + + // #### Issuing semi-fungible tokens using the factory + // ```js + { + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForIssuingSemiFungible(alice.address, { + tokenName: "NEWSEMI", + tokenTicker: "SEMI", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier // md-as-comment + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueSemiFungible(transactionOnNetwork); + + const tokenIdentifier = outcome[0].tokenIdentifier; + } + // ``` + + // #### Issuing NFT collection & creating NFTs using the controller + + // ```js + { + // create the entrypoint and the token management controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + let transaction = await controller.createTransactionForIssuingNonFungible( + alice, + alice.getNonceThenIncrement(), + { + tokenName: "NEWNFT", + tokenTicker: "NFT", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }, + ); + + // sending the transaction // md-as-comment + let txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + let outcome = await controller.awaitCompletedIssueNonFungible(txHash); + + const collectionIdentifier = outcome[0].tokenIdentifier; + + // create an NFT // md-as-comment + transaction = await controller.createTransactionForCreatingNft(alice, alice.getNonceThenIncrement(), { + tokenIdentifier: "FRANK-aa9e8d", + initialQuantity: 1n, + name: "test", + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + }); + + // sending the transaction // md-as-comment + txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + outcome = await controller.awaitCompletedCreateNft(txHash); + + const identifier = outcome[0].tokenIdentifier; + } + // ``` + + // #### Issuing NFT collection & creating NFTs using the factory + // ```js + { + // create the entrypoint and the token management transdactions factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token // md-as-comment + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + let transaction = await factory.createTransactionForIssuingNonFungible(alice.address, { + tokenName: "NEWNFT", + tokenTicker: "NFT", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }); + // fetch the nonce of the network // md-as-comment + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + let txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier // md-as-comment + let transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier // md-as-comment + let parser = new TokenManagementTransactionsOutcomeParser(); + let outcome = parser.parseIssueNonFungible(transactionOnNetwork); + + const collectionIdentifier = outcome[0].tokenIdentifier; + + transaction = await factory.createTransactionForCreatingNFT(alice.address, { + tokenIdentifier: "FRANK-aa9e8d", + initialQuantity: 1n, + name: "test", + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + }); + + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction // md-as-comment + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction // md-as-comment + txHash = await entrypoint.sendTransaction(transaction); + + // ### wait for transaction to execute, extract the token identifier // md-as-comment + transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + outcome = parser.parseIssueNonFungible(transactionOnNetwork); + + const identifier = outcome[0].tokenIdentifier; + } + // ``` + + // These are just a few examples of what you can do using the token management controller or factory. For a complete list of supported methods, please refer to the autogenerated documentation: + + // - [TokenManagementController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementController.html) + // - [TokenManagementTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementTransactionsFactory.html) +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/transactions.js b/cookbook/transactions.js deleted file mode 100644 index a7d014da..00000000 --- a/cookbook/transactions.js +++ /dev/null @@ -1,205 +0,0 @@ - -import { Account, DevnetEntrypoint, Token, TokenTransfer, TransactionsFactoryConfig, TransfersController, TransferTransactionsFactory } from '@multiversx/sdk-core'; // md-ignore -import path from 'path'; // md-ignore -// ## Creating transactions - -// In this section, we’ll explore how to create different types of transactions. To create transactions, we can use either controllers or factories. -// Controllers are ideal for quick scripts or network interactions, while factories provide a more granular and lower-level approach, typically required for DApps. - -// Controllers typically use the same parameters as factories, but they also require an Account object and the sender’s nonce. -// Controllers also include extra functionality, such as waiting for transaction completion and parsing transactions. -// The same functionality can be achieved for transactions built using factories, and we’ll see how in the sections below. In the next section, we’ll learn how to create transactions using both methods. - -// ### Instantiating Controllers and Factories -// There are two ways to create controllers and factories: -// 1. Get them from the entrypoint. -// 2. Manually instantiate them. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - - // getting the controller and the factory from the entrypoint - const transfersController = entrypoint.createTransfersController(); - const transfersFactory = entrypoint.createTransfersTransactionsFactory(); - - // manually instantiating the controller and the factory - const controller = new TransfersController({ chainID: 'D' }); - - const config = new TransactionsFactoryConfig({ chainID: 'D' }); - const factory = new TransferTransactionsFactory({ config }); -} -// ``` - -// ### Token transfers -// We can send both native tokens (EGLD) and ESDT tokens using either the controller or the factory. -// #### Native Token Transfers Using the Controller -// When using the controller, the transaction will be signed because we’ll be working with an Account. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transfersController = entrypoint.createTransfersController(); - const transaction = await transfersController.createTransactionForTransfer( - alice, - alice.getNonceThenIncrement(), - { - receiver: bob, - nativeAmount: 1n, - }, - ); - - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. - -// #### Native Token Transfers Using the Factory -// When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. -// You will need to handle these aspects after the transaction is created. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTransfersTransactionsFactory(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - const transaction = factory.createTransactionForTransfer(alice, { - receiver: bob, - nativeAmount: 1000000000000000000n, - }); - - // set the sender's nonce - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction using the sender's account - transaction.signature = await alice.signTransaction(transaction); - - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. - -// #### Custom token transfers using the controller - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const esdt = new Token({ identifier: "TEST-123456" }); - const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); - - const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); - const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); - - const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); - const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); - - const transfersController = entrypoint.createTransfersController(); - const transaction = transfersController.createTransactionForTransfer(alice, alice.getNonceThenIncrement(), { - receiver: bob, - tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], - }); - - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. - -// #### Custom token transfers using the factory -// When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. These aspects should be handled after the transaction is created. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTransfersTransactionsFactory(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const esdt = new Token({ identifier: "TEST-123456" }); // fungible tokens don't have a nonce - const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); // we set the desired amount we want to send - - const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); - const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); // for NFTs we set the amount to `1` - - const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); - const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); // for SFTs we set the desired amount we want to send - - const transaction = factory.createTransactionForTransfer(alice, { - receiver: bob, - tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], - }); - - // set the sender's nonce - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction using the sender's account - transaction.signature = await alice.signTransaction(transaction); - - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` - -// If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. - -// #### Sending native and custom tokens -// Both native and custom tokens can now be sent. If a `nativeAmount` is provided along with `tokenTransfers`, the native token will be included in the `MultiESDTNFTTransfer` built-in function call. -// We can send both types of tokens using either the `controller` or the `factory`, but for simplicity, we’ll use the controller in this example. - -// ```js -{ - const entrypoint = new DevnetEntrypoint(); - - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const esdt = new Token({ identifier: "TEST-123456" }); - const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); - - const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); - const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); - - const transfersController = entrypoint.createTransfersController(); - const transaction = transfersController.createTransactionForTransfer(alice, alice.getNonceThenIncrement(), { - receiver: bob, - nativeAmount: 1000000000000000000n, - tokenTransfers: [firstTransfer, secondTransfer], - }); - - const txHash = await entrypoint.sendTransaction(transaction); -} -// ``` diff --git a/cookbook/transactions.ts b/cookbook/transactions.ts new file mode 100644 index 00000000..c06f1162 --- /dev/null +++ b/cookbook/transactions.ts @@ -0,0 +1,225 @@ +import path from "path"; // md-ignore +import { + Account, + Address, + DevnetEntrypoint, + Token, + TokenTransfer, + TransactionsFactoryConfig, + TransfersController, + TransferTransactionsFactory, +} from "../src"; // md-ignore +(async () => { + // ## Creating transactions + + // In this section, we’ll explore how to create different types of transactions. To create transactions, we can use either controllers or factories. + // Controllers are ideal for quick scripts or network interactions, while factories provide a more granular and lower-level approach, typically required for DApps. + + // Controllers typically use the same parameters as factories, but they also require an Account object and the sender’s nonce. + // Controllers also include extra functionality, such as waiting for transaction completion and parsing transactions. + // The same functionality can be achieved for transactions built using factories, and we’ll see how in the sections below. In the next section, we’ll learn how to create transactions using both methods. + + // ### Instantiating Controllers and Factories + // There are two ways to create controllers and factories: + // 1. Get them from the entrypoint. + // 2. Manually instantiate them. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + + // getting the controller and the factory from the entrypoint + const transfersController = entrypoint.createTransfersController(); + const transfersFactory = entrypoint.createTransfersTransactionsFactory(); + + // manually instantiating the controller and the factory + const controller = new TransfersController({ chainID: "D" }); + + const config = new TransactionsFactoryConfig({ chainID: "D" }); + const factory = new TransferTransactionsFactory({ config }); + } + // ``` + + // ### Token transfers + // We can send both native tokens (EGLD) and ESDT tokens using either the controller or the factory. + // #### Native Token Transfers Using the Controller + // When using the controller, the transaction will be signed because we’ll be working with an Account. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const transfersController = entrypoint.createTransfersController(); + const transaction = await transfersController.createTransactionForTransfer( + alice, + alice.getNonceThenIncrement(), + { + receiver: bob, + nativeAmount: 1n, + }, + ); + + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. + + // #### Native Token Transfers Using the Factory + // When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. + // You will need to handle these aspects after the transaction is created. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTransfersTransactionsFactory(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const transaction = factory.createTransactionForTransfer(alice.address, { + receiver: bob, + nativeAmount: 1000000000000000000n, + }); + + // set the sender's nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction using the sender's account + transaction.signature = await alice.signTransaction(transaction); + + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. + + // #### Custom token transfers using the controller + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const esdt = new Token({ identifier: "TEST-123456" }); + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); + + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); + + const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); + const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); + + const transfersController = entrypoint.createTransfersController(); + const transaction = await transfersController.createTransactionForTransfer( + alice, + alice.getNonceThenIncrement(), + { + receiver: bob, + tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], + }, + ); + + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. + + // #### Custom token transfers using the factory + // When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. These aspects should be handled after the transaction is created. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTransfersTransactionsFactory(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const esdt = new Token({ identifier: "TEST-123456" }); // fungible tokens don't have a nonce + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); // we set the desired amount we want to send + + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); // for NFTs we set the amount to `1` + + const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); + const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); // for SFTs we set the desired amount we want to send + + const transaction = factory.createTransactionForTransfer(alice.address, { + receiver: bob, + tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], + }); + + // set the sender's nonce + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction using the sender's account + transaction.signature = await alice.signTransaction(transaction); + + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. + + // #### Sending native and custom tokens + // Both native and custom tokens can now be sent. If a `nativeAmount` is provided along with `tokenTransfers`, the native token will be included in the `MultiESDTNFTTransfer` built-in function call. + // We can send both types of tokens using either the `controller` or the `factory`, but for simplicity, we’ll use the controller in this example. + + // ```js + { + const entrypoint = new DevnetEntrypoint(); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + + const esdt = new Token({ identifier: "TEST-123456" }); + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); + + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); + + const transfersController = entrypoint.createTransfersController(); + const transaction = await transfersController.createTransactionForTransfer( + alice, + alice.getNonceThenIncrement(), + { + receiver: bob, + nativeAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer], + }, + ); + + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/verifySIgnatures.js b/cookbook/verifySIgnatures.js deleted file mode 100644 index 808f5cd3..00000000 --- a/cookbook/verifySIgnatures.js +++ /dev/null @@ -1,147 +0,0 @@ - -import { Account, Address, Message, MessageComputer, Transaction, TransactionComputer, UserPublicKey, UserVerifier } from "@multiversx/sdk-core"; // md-ignore -import path from 'path'; // md-ignore -// ## Verifying signatures - -// Signature verification is performed using an account’s public key. -// To simplify this process, we provide wrappers over public keys that make verification easier and more convenient. - -// #### Verifying Transaction signature using a UserVerifier - -// ```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const account = await Account.newFromPem(filePath); - - const transaction = new Transaction({ - nonce: 90n, - sender: account.address, - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - value: 1000000000000000000n, - gasLimit: 50000n, - chainID: "D" - }); - - // sign and apply the signature on the transaction // md-as-comment - transaction.signature = await account.sign(transaction); - - // instantiating a user verifier; basically gets the public key // md-as-comment - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const aliceVerifier = UserVerifier.fromAddress(alice); - - // serialize the transaction for verification // md-as-comment - const transactionComputer = new TransactionComputer(); - const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); - - // verify the signature // md-as-comment - const isSignedByAlice = aliceVerifier.verify(serializedTransaction, transaction.signature); - - - console.log("Transaction is signed by Alice: ", isSignedByAlice); -} -// ``` - -// #### Verifying Message signature using a UserVerifier - -// ```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const account = await Account.newFromPem(filePath); - - const message = new Message({ - data: new Uint8Array(Buffer.from("hello")), - address: account.address - }); - - // sign and apply the signature on the message // md-as-comment - message.signature = await account.sign(message); - - // instantiating a user verifier; basically gets the public key // md-as-comment - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const aliceVerifier = UserVerifier.fromAddress(alice); - - // serialize the message for verification // md-as-comment - const messageComputer = new MessageComputer(); - const serializedMessage = messageComputer.computeBytesForVerifying(message); - - // verify the signature // md-as-comment - const isSignedByAlice = aliceVerifier.verify(serializedMessage, message.signature); - - console.log("Message is signed by Alice: ", isSignedByAlice); -} -// ``` - -// #### Verifying a signature using a public key -// ```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const account = await Account.newFromPem(filePath); - - const transaction = new Transaction({ - nonce: 90n, - sender: account.address, - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - value: 1000000000000000000n, - gasLimit: 50000n, - chainID: "D" - }); - - // sign and apply the signature on the transaction // md-as-comment - transaction.signature = await account.sign(transaction); - - // instantiating a public key // md-as-comment - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const publicKey = new UserPublicKey(alice.getPublicKey()); - - // serialize the transaction for verification // md-as-comment - const transactionComputer = new TransactionComputer(); - const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); - - // verify the signature // md-as-comment - const isSignedByAlice = publicKey.verify(serializedTransaction, transaction.signature); - console.log("Transaction is signed by Alice: ", isSignedByAlice); -} -// ``` - -// #### Sending messages over boundaries -// Signed Message objects are typically sent to a remote party (e.g., a service), which can then verify the signature. -// To prepare a message for transmission, you can use the `MessageComputer.packMessage()` utility method. - -// ```js -{ - const filePath = path.join("src", "testdata", "testwallets", "alice.pem"); - const account = await Account.newFromPem(filePath); - - const message = new Message({ - data: new Uint8Array(Buffer.from("hello")), - address: account.address - }); - - // sign and apply the signature on the message // md-as-comment - message.signature = await account.sign(message); - - const messageComputer = new MessageComputer(); - const packedMessage = messageComputer.packMessage(message); - - console.log("Packed message", packedMessage); -} -// ``` - -// Then, on the receiving side, you can use `func:MessageComputer.unpackMessage()` to reconstruct the message, prior verification: - -// ```js -{ - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - - const messageComputer = new MessageComputer(); - - // restore message // md-as-comment - const message = messageComputer.unpackMessage(packedMessage); - - // verify the signature // md-as-comment - const publicKey = new UserPublicKey(alice.getPublicKey()); - const isSignedByAlice = publicKey.verify(messageComputer.computeBytesForVerifying(message), message.signature); - - console.log("Transaction is signed by Alice: ", isSignedByAlice); -} -// ``` diff --git a/cookbook/verifySignatures.ts b/cookbook/verifySignatures.ts new file mode 100644 index 00000000..d9ef3d48 --- /dev/null +++ b/cookbook/verifySignatures.ts @@ -0,0 +1,165 @@ +import path from "path"; // md-ignore +import { + Account, + Address, + Message, + MessageComputer, + Transaction, + TransactionComputer, + UserPublicKey, + UserVerifier, +} from "../src"; // md-ignore +(async () => { + // ## Verifying signatures + + // Signature verification is performed using an account’s public key. + // To simplify this process, we provide wrappers over public keys that make verification easier and more convenient. + + // #### Verifying Transaction signature using a UserVerifier + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + nonce: 90n, + sender: account.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D", + }); + + // sign and apply the signature on the transaction // md-as-comment + transaction.signature = await account.signTransaction(transaction); + + // instantiating a user verifier; basically gets the public key // md-as-comment + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const aliceVerifier = UserVerifier.fromAddress(alice); + + // serialize the transaction for verification // md-as-comment + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); + + // verify the signature // md-as-comment + const isSignedByAlice = aliceVerifier.verify(serializedTransaction, transaction.signature); + + console.log("Transaction is signed by Alice: ", isSignedByAlice); + } + // ``` + + // #### Verifying Message signature using a UserVerifier + + // ```ts + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); + + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: account.address, + }); + + // sign and apply the signature on the message // md-as-comment + message.signature = await account.signMessage(message); + + // instantiating a user verifier; basically gets the public key // md-as-comment + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const aliceVerifier = UserVerifier.fromAddress(alice); + + // serialize the message for verification // md-as-comment + const messageComputer = new MessageComputer(); + const serializedMessage = messageComputer.computeBytesForVerifying(message); + + // verify the signature // md-as-comment + const isSignedByAlice = await aliceVerifier.verify(serializedMessage, message.signature); + + console.log("Message is signed by Alice: ", isSignedByAlice); + } + // ``` + + // #### Verifying a signature using a public key + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); + + const transaction = new Transaction({ + nonce: 90n, + sender: account.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D", + }); + + // sign and apply the signature on the transaction // md-as-comment + transaction.signature = await account.signTransaction(transaction); + + // instantiating a public key // md-as-comment + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const publicKey = new UserPublicKey(alice.getPublicKey()); + + // serialize the transaction for verification // md-as-comment + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); + + // verify the signature // md-as-comment + const isSignedByAlice = await publicKey.verify(serializedTransaction, transaction.signature); + console.log("Transaction is signed by Alice: ", isSignedByAlice); + } + // ``` + + // #### Sending messages over boundaries + // Signed Message objects are typically sent to a remote party (e.g., a service), which can then verify the signature. + // To prepare a message for transmission, you can use the `MessageComputer.packMessage()` utility method. + + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); + + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: account.address, + }); + + // sign and apply the signature on the message // md-as-comment + message.signature = await account.signMessage(message); + + const messageComputer = new MessageComputer(); + const packedMessage = messageComputer.packMessage(message); + + console.log("Packed message", packedMessage); + } + // ``` + + // Then, on the receiving side, you can use `func:MessageComputer.unpackMessage()` to reconstruct the message, prior verification: + + // ```js + { + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const messageComputer = new MessageComputer(); + const data = Buffer.from("test"); + + const message = new Message({ + data: data, + address: alice.address, + }); + + message.signature = await alice.signMessage(message); + // restore message // md-as-comment + + const packedMessage = messageComputer.packMessage(message); + const unpackedMessage = messageComputer.unpackMessage(packedMessage); + + // verify the signature // md-as-comment + const isSignedByAlice = await alice.verifyMessageSignature(unpackedMessage, message.signature); + + console.log("Transaction is signed by Alice: ", isSignedByAlice); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); diff --git a/cookbook/wallet.pem b/cookbook/wallet.pem new file mode 100644 index 00000000..d27bb68b --- /dev/null +++ b/cookbook/wallet.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY for erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th----- +NDEzZjQyNTc1ZjdmMjZmYWQzMzE3YTc3ODc3MTIxMmZkYjgwMjQ1ODUwOTgxZTQ4 +YjU4YTRmMjVlMzQ0ZThmOTAxMzk0NzJlZmY2ODg2NzcxYTk4MmYzMDgzZGE1ZDQy +MWYyNGMyOTE4MWU2Mzg4ODIyOGRjODFjYTYwZDY5ZTE= +-----END PRIVATE KEY for erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th----- \ No newline at end of file diff --git a/cookbook/wallets.js b/cookbook/wallets.js deleted file mode 100644 index 043ff66b..00000000 --- a/cookbook/wallets.js +++ /dev/null @@ -1,141 +0,0 @@ - -import { KeyPair, Mnemonic, UserPem, UserWallet } from "@multiversx/sdk-core"; // md-ignore -import path from 'path'; // md-ignore -// ## Wallets - -// #### Generating a mnemonic -// Mnemonic generation is based on [bip39](https://www.npmjs.com/package/bip39) and can be achieved as follows: - -// ``` js - -const mnemonic = Mnemonic.generate(); -const words = mnemonic.getWords(); -console.log({ words }); -// ``` - -// #### Saving the mnemonic to a keystore file -// The mnemonic can be saved to a keystore file: - -// ``` js -{ - const mnemonic = Mnemonic.generate(); - - // saves the mnemonic to a keystore file with kind=mnemonic // md-as-comment - const wallet = UserWallet.fromMnemonic({ mnemonic: mnemonic.getText(), password: "password" }); - - const filePath = path.join("src", "testdata", "testwallets", "walletWithMnemonic.json"); - wallet.save(filePath); -} -// ``` - -// #### Deriving secret keys from a mnemonic -// Given a mnemonic, we can derive keypairs: - -// ``` js -{ - const mnemonic = Mnemonic.generate(); - - const secretKey = mnemonic.deriveKey(0); - const publicKey = secretKey.generatePublicKey(); - - console.log("Secret key: ", secretKey.hex()); - console.log("Public key: ", publicKey.hex()); -} -// ``` - -// #### Saving a secret key to a keystore file -// The secret key can also be saved to a keystore file: - -// ``` js -{ - const mnemonic = Mnemonic.generate(); - const secretKey = mnemonic.deriveKey(); - - const wallet = UserWallet.fromSecretKey({ secretKey: secretKey, password: "password" }); - - const filePath = path.join("src", "testdata", "testwallets", "walletWithSecretKey.json"); - wallet.save(filePath); -} -// ``` - -// #### Saving a secret key to a PEM file -// We can save a secret key to a pem file. *This is not recommended as it is not secure, but it's very convenient for testing purposes.* - -// ``` js -{ - const mnemonic = Mnemonic.generate(); - - // by default, derives using the index = 0 // md-as-comment - const secretKey = mnemonic.deriveKey(); - const publicKey = secretKey.generatePublicKey(); - - const label = publicKey.toAddress().toBech32(); - const pem = new UserPem(label, secretKey); - - const filePath = path.join("src", "testdata", "testwallets", "wallet.pem"); - pem.save(filePath); -} -// ``` - -// #### Generating a KeyPair -// A `KeyPair` is a wrapper over a secret key and a public key. We can create a keypair and use it for signing or verifying. - -// ``` js -{ - const keypair = KeyPair.generate(); - - // by default, derives using the index = 0 // md-as-comment - const secretKey = keypair.getSecretKey(); - const publicKey = keypair.getPublicKey(); -} -// ``` - -// #### Loading a wallet from keystore mnemonic file -// Load a keystore that holds an encrypted mnemonic (and perform wallet derivation at the same time): - -// ``` js -{ - const filePath = path.join("src", "testdata", "testwallets", "walletWithMnemonic.json"); - - // loads the mnemonic and derives the a secret key; default index = 0 // md-as-comment - let secretKey = UserWallet.loadSecretKey(filePath, "password"); - let address = secretKey.generatePublicKey().toAddress('erd'); - - console.log("Secret key: ", secretKey.hex()); - console.log("Address: ", address.toBech32()); - - // derive secret key with index = 7 // md-as-comment - secretKey = UserWallet.loadSecretKey(path, "password", 7); - address = secretKey.generatePublicKey().toAddress(); - - console.log("Secret key: ", secretKey.hex()); - console.log("Address: ", address.toBech32()); -} -// ``` - -// #### Loading a wallet from a keystore secret key file - -// ``` js -{ - const filePath = path.join("src", "testdata", "testwallets", "walletWithSecretKey.json"); - - let secretKey = UserWallet.loadSecretKey(filePath, "password"); - let address = secretKey.generatePublicKey().toAddress('erd'); - - console.log("Secret key: ", secretKey.hex()); - console.log("Address: ", address.toBech32()); -} -// ``` - -// #### Loading a wallet from a PEM file - -// ``` js -{ - const filePath = path.join("src", "testdata", "testwallets", "wallet.pem"); - - let pem = UserPem.fromFile(filePath); - - console.log("Secret key: ", pem.secretKey.hex()); - console.log("Public key: ", pem.publicKey.hex()); -} -// ``` \ No newline at end of file diff --git a/cookbook/wallets.ts b/cookbook/wallets.ts new file mode 100644 index 00000000..d99c88b5 --- /dev/null +++ b/cookbook/wallets.ts @@ -0,0 +1,145 @@ +import path from "path"; // md-ignore +import { KeyPair, Mnemonic, UserPem, UserWallet } from "../src"; // md-ignore +(async () => { + // ## Wallets + + // #### Generating a mnemonic + // Mnemonic generation is based on [bip39](https://www.npmjs.com/package/bip39) and can be achieved as follows: + + // ``` js + + const mnemonic = Mnemonic.generate(); + const words = mnemonic.getWords(); + console.log({ words }); + // ``` + + // #### Saving the mnemonic to a keystore file + // The mnemonic can be saved to a keystore file: + + // ``` js + { + const mnemonic = Mnemonic.generate(); + + // saves the mnemonic to a keystore file with kind=mnemonic // md-as-comment + const wallet = UserWallet.fromMnemonic({ mnemonic: mnemonic.toString(), password: "password" }); + + const filePath = path.join("../src", "testdata", "testwallets", "walletWithMnemonic.json"); + console.log({ filePath }); + wallet.save(filePath); + } + // ``` + + // #### Deriving secret keys from a mnemonic + // Given a mnemonic, we can derive keypairs: + + // ``` js + { + const mnemonic = Mnemonic.generate(); + + const secretKey = mnemonic.deriveKey(0); + const publicKey = secretKey.generatePublicKey(); + + console.log("Secret key: ", secretKey.hex()); + console.log("Public key: ", publicKey.hex()); + } + // ``` + + // #### Saving a secret key to a keystore file + // The secret key can also be saved to a keystore file: + + // ``` js + { + const mnemonic = Mnemonic.generate(); + const secretKey = mnemonic.deriveKey(); + + const wallet = UserWallet.fromSecretKey({ secretKey: secretKey, password: "password" }); + + const filePath = path.join("../src", "testdata", "testwallets", "walletWithSecretKey.json"); + wallet.save(filePath); + } + // ``` + + // #### Saving a secret key to a PEM file + // We can save a secret key to a pem file. *This is not recommended as it is not secure, but it's very convenient for testing purposes.* + + // ``` js + { + const mnemonic = Mnemonic.generate(); + + // by default, derives using the index = 0 // md-as-comment + const secretKey = mnemonic.deriveKey(); + const publicKey = secretKey.generatePublicKey(); + + const label = publicKey.toAddress().toBech32(); + const pem = new UserPem(label, secretKey); + + const filePath = path.join("../src", "testdata", "testwallets", "wallet.pem"); + pem.save(filePath); + } + // ``` + + // #### Generating a KeyPair + // A `KeyPair` is a wrapper over a secret key and a public key. We can create a keypair and use it for signing or verifying. + + // ``` js + { + const keypair = KeyPair.generate(); + + // by default, derives using the index = 0 // md-as-comment + const secretKey = keypair.getSecretKey(); + const publicKey = keypair.getPublicKey(); + } + // ``` + + // #### Loading a wallet from keystore mnemonic file + // Load a keystore that holds an encrypted mnemonic (and perform wallet derivation at the same time): + + // ``` js + { + const filePath = path.join("../src", "testdata", "testwallets", "walletWithMnemonic.json"); + + // loads the mnemonic and derives the a secret key; default index = 0 // md-as-comment + let secretKey = UserWallet.loadSecretKey(filePath, "password"); + let address = secretKey.generatePublicKey().toAddress("erd"); + + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); + + // derive secret key with index = 7 // md-as-comment + secretKey = UserWallet.loadSecretKey(filePath, "password", 7); + address = secretKey.generatePublicKey().toAddress(); + + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); + } + // ``` + + // #### Loading a wallet from a keystore secret key file + + // ``` js + { + const filePath = path.join("../src", "testdata", "testwallets", "walletWithSecretKey.json"); + + let secretKey = UserWallet.loadSecretKey(filePath, "password"); + let address = secretKey.generatePublicKey().toAddress("erd"); + + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); + } + // ``` + + // #### Loading a wallet from a PEM file + + // ``` js + { + const filePath = path.join("../src", "testdata", "testwallets", "wallet.pem"); + + let pem = UserPem.fromFile(filePath); + + console.log("Secret key: ", pem.secretKey.hex()); + console.log("Public key: ", pem.publicKey.hex()); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); diff --git a/src/wallet/userWallet.ts b/src/wallet/userWallet.ts index 70712d33..3cd6e43b 100644 --- a/src/wallet/userWallet.ts +++ b/src/wallet/userWallet.ts @@ -50,7 +50,9 @@ export class UserWallet { randomness = randomness || new Randomness(); const publicKey = secretKey.generatePublicKey(); + console.log({ secretKey, v: secretKey.valueOf() }); const data = Buffer.concat([secretKey.valueOf(), publicKey.valueOf()]); + console.log({ data }); const encryptedData = Encryptor.encrypt(data, password, randomness); return new UserWallet({ @@ -250,6 +252,7 @@ export class UserWallet { : resolve(join(process.cwd(), path.toString())); const jsonContent = this.toJSON(addressHrp); + console.log({ path }); writeFileSync(resolvedPath, jsonContent, { encoding: "utf-8" }); } } From 18f80eeebd5b624659409cc9ef796eefd40d2a08 Mon Sep 17 00:00:00 2001 From: danielailie Date: Thu, 17 Apr 2025 14:22:23 +0300 Subject: [PATCH 06/15] Bump version --- package-lock.json | 4 ++-- package.json | 2 +- src/wallet/userWallet.ts | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 12a2eb43..8bc6faee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@multiversx/sdk-core", - "version": "14.0.2", + "version": "14.0.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@multiversx/sdk-core", - "version": "14.0.2", + "version": "14.0.3", "license": "MIT", "dependencies": { "@multiversx/sdk-transaction-decoder": "1.0.2", diff --git a/package.json b/package.json index 1e5d2d8b..05d65752 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@multiversx/sdk-core", - "version": "14.0.2", + "version": "14.0.3", "description": "MultiversX SDK for JavaScript and TypeScript", "author": "MultiversX", "homepage": "https://multiversx.com", diff --git a/src/wallet/userWallet.ts b/src/wallet/userWallet.ts index 3cd6e43b..cc301723 100644 --- a/src/wallet/userWallet.ts +++ b/src/wallet/userWallet.ts @@ -252,7 +252,6 @@ export class UserWallet { : resolve(join(process.cwd(), path.toString())); const jsonContent = this.toJSON(addressHrp); - console.log({ path }); - writeFileSync(resolvedPath, jsonContent, { encoding: "utf-8" }); + writeFileSync(resolvedPath, JSON.stringify(jsonContent, null, 4), { encoding: "utf-8" }); } } From f79df0381d3f879e5e09816c97ae20ac594abd56 Mon Sep 17 00:00:00 2001 From: danielailie Date: Thu, 17 Apr 2025 14:30:13 +0300 Subject: [PATCH 07/15] Fixed tokens --- cookbook/cookbook.md | 8 +++++--- cookbook/tokens.ts | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cookbook/cookbook.md b/cookbook/cookbook.md index b1d25238..ac09f671 100644 --- a/cookbook/cookbook.md +++ b/cookbook/cookbook.md @@ -1709,7 +1709,7 @@ import { loadAbiRegistry } from "../src/testutils"; let txHash = await entrypoint.sendTransaction(transaction); // wait for transaction to execute, extract the token identifier - let outcome = await controller.awaitCompletedIssueNonFungible(txHash); + const outcome = await controller.awaitCompletedIssueNonFungible(txHash); const collectionIdentifier = outcome[0].tokenIdentifier; @@ -1728,9 +1728,11 @@ import { loadAbiRegistry } from "../src/testutils"; txHash = await entrypoint.sendTransaction(transaction); // wait for transaction to execute, extract the token identifier - outcome = await controller.awaitCompletedCreateNft(txHash); + const outcomeNft = await controller.awaitCompletedCreateNft(txHash); - const identifier = outcome[0].tokenIdentifier; + const identifier = outcomeNft[0].tokenIdentifier; + const nonce = outcomeNft[0].nonce; + const initialQuantity = outcomeNft[0].initialQuantity; } // ``` diff --git a/cookbook/tokens.ts b/cookbook/tokens.ts index 37bb1f5a..557e310b 100644 --- a/cookbook/tokens.ts +++ b/cookbook/tokens.ts @@ -294,7 +294,7 @@ import { Account, Address, DevnetEntrypoint, TokenManagementTransactionsOutcomeP let txHash = await entrypoint.sendTransaction(transaction); // wait for transaction to execute, extract the token identifier // md-as-comment - let outcome = await controller.awaitCompletedIssueNonFungible(txHash); + const outcome = await controller.awaitCompletedIssueNonFungible(txHash); const collectionIdentifier = outcome[0].tokenIdentifier; @@ -313,9 +313,11 @@ import { Account, Address, DevnetEntrypoint, TokenManagementTransactionsOutcomeP txHash = await entrypoint.sendTransaction(transaction); // wait for transaction to execute, extract the token identifier // md-as-comment - outcome = await controller.awaitCompletedCreateNft(txHash); + const outcomeNft = await controller.awaitCompletedCreateNft(txHash); - const identifier = outcome[0].tokenIdentifier; + const identifier = outcomeNft[0].tokenIdentifier; + const nonce = outcomeNft[0].nonce; + const initialQuantity = outcomeNft[0].initialQuantity; } // ``` From d10daa219addd5b8bc81b982c9aad92e7ab08e1d Mon Sep 17 00:00:00 2001 From: danielailie Date: Thu, 17 Apr 2025 15:01:09 +0300 Subject: [PATCH 08/15] Update cookbook --- cookbook/account.ts | 1 + cookbook/accountManagement.ts | 1 + cookbook/addresses.ts | 1 + cookbook/cookbook.md | 1329 ++++++++++++++++----------------- cookbook/delegation.ts | 1 + cookbook/entrypoints.ts | 1 + cookbook/generate.py | 37 +- cookbook/guarded.ts | 1 + cookbook/networkProviders.ts | 1 + cookbook/relayed.ts | 1 + cookbook/signingObjects.ts | 1 + cookbook/smartContracts.ts | 1 + cookbook/tokens.ts | 1 + cookbook/transactions.ts | 1 + cookbook/verifySignatures.ts | 1 + cookbook/wallets.ts | 1 + 16 files changed, 654 insertions(+), 726 deletions(-) diff --git a/cookbook/account.ts b/cookbook/account.ts index bdad9bba..2d50098b 100644 --- a/cookbook/account.ts +++ b/cookbook/account.ts @@ -1,5 +1,6 @@ import path from "path"; // md-ignore import { Account, DevnetEntrypoint, KeyPair, Mnemonic, UserSecretKey } from "../src"; // md-ignore +// md-start (async () => { // ## Creating Accounts diff --git a/cookbook/accountManagement.ts b/cookbook/accountManagement.ts index b7a8b3f9..cac55396 100644 --- a/cookbook/accountManagement.ts +++ b/cookbook/accountManagement.ts @@ -1,5 +1,6 @@ import path from "path"; // md-ignore import { Account, Address, DevnetEntrypoint } from "../src"; // md-ignore +// md-start (async () => { // ### Account management diff --git a/cookbook/addresses.ts b/cookbook/addresses.ts index 2c49cb8f..197bab11 100644 --- a/cookbook/addresses.ts +++ b/cookbook/addresses.ts @@ -1,4 +1,5 @@ import { Address, AddressComputer, LibraryConfig } from "../src"; // md-ignore +// md-start (async () => { // ## Addresses diff --git a/cookbook/cookbook.md b/cookbook/cookbook.md index ac09f671..ace1d92f 100644 --- a/cookbook/cookbook.md +++ b/cookbook/cookbook.md @@ -1,99 +1,98 @@ -(async () => { - // ## Overview +md-start - // This guide walks you through handling common tasks using the MultiversX Javascript SDK (v14, latest stable version). +## Overview - // ## Creating an Entrypoint +This guide walks you through handling common tasks using the MultiversX Javascript SDK (v14, latest stable version). - // An Entrypoint represents a network client that simplifies access to the most common operations. - // There is a dedicated entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint`, `LocalnetEntrypoint`. +## Creating an Entrypoint - // For example, to create a Devnet entrypoint you have the following command: +An Entrypoint represents a network client that simplifies access to the most common operations. +There is a dedicated entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint`, `LocalnetEntrypoint`. - // ```js +For example, to create a Devnet entrypoint you have the following command: + +```js const entrypoint = new DevnetEntrypoint(); - // ``` +``` - // #### Using a Custom API - // If you'd like to connect to a third-party API, you can specify the url parameter: +#### Using a Custom API +If you'd like to connect to a third-party API, you can specify the url parameter: - // ```js +```js const apiEntrypoint = new DevnetEntrypoint("https://custom-multiversx-devnet-api.com"); - // ``` +``` - // #### Using a Proxy +#### Using a Proxy - // By default, the DevnetEntrypoint uses the standard API. However, you can create a custom entrypoint that interacts with a proxy by specifying the kind parameter: +By default, the DevnetEntrypoint uses the standard API. However, you can create a custom entrypoint that interacts with a proxy by specifying the kind parameter: - // ```js +```js const customEntrypoint = new DevnetEntrypoint("https://devnet-gateway.multiversx.com", "proxy"); - // ``` -})().catch((e) => { - console.log({ e }); -}); +``` + +md-start -(async () => { - // ## Creating Accounts +## Creating Accounts - // You can initialize an account directly from the entrypoint. Keep in mind that the account is network agnostic, meaning it doesn't matter which entrypoint is used. - // Accounts are used for signing transactions and messages and managing the account's nonce. They can also be saved to a PEM or keystore file for future use. +You can initialize an account directly from the entrypoint. Keep in mind that the account is network agnostic, meaning it doesn't matter which entrypoint is used. +Accounts are used for signing transactions and messages and managing the account's nonce. They can also be saved to a PEM or keystore file for future use. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const account = entrypoint.createAccount(); } - // ``` +``` - // ### Other Ways to Instantiate an Account +### Other Ways to Instantiate an Account - // #### From a Secret Key - // ```js +#### From a Secret Key +```js { const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); const accountFromSecretKey = new Account(secretKey); } - // ``` +``` - // #### From a PEM file - // ```js +#### From a PEM file +```js { const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); const accountFromPem = Account.newFromPem(filePath); } - // ``` +``` - // #### From a Keystore File - // ```js +#### From a Keystore File +```js { const keystorePath = path.join("../src", "testdata", "testwallets", "alice.json"); const accountFromKeystore = Account.newFromKeystore(keystorePath, "password"); } - // ``` +``` - // #### From a Mnemonic - // ```js +#### From a Mnemonic +```js const mnemonic = Mnemonic.generate(); const accountFromMnemonic = Account.newFromMnemonic(mnemonic.toString()); - // ``` +``` - // #### From a KeyPair +#### From a KeyPair - // ```js +```js const keypair = KeyPair.generate(); const accountFromKeyPairs = Account.newFromKeypair(keypair); - // ``` +``` - // ### Managing the Account Nonce +### Managing the Account Nonce - // An account has a `nonce` property that the user is responsible for managing. - // You can fetch the nonce from the network and increment it after each transaction. - // Each transaction must have the correct nonce, otherwise it will fail to execute. +An account has a `nonce` property that the user is responsible for managing. +You can fetch the nonce from the network and increment it after each transaction. +Each transaction must have the correct nonce, otherwise it will fail to execute. - // ```js +```js { const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; const key = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); @@ -109,18 +108,18 @@ // Increment nonce after each transaction const nonce = accountWithNonce.getNonceThenIncrement(); } - // ``` +``` - // For more details, see the [Creating Transactions](#creating-transactions) section. +For more details, see the [Creating Transactions](#creating-transactions) section. - // #### Saving the Account to a File +#### Saving the Account to a File - // Accounts can be saved to either a PEM file or a keystore file. - // While PEM wallets are less secure for storing secret keys, they are convenient for testing purposes. - // Keystore files offer a higher level of security. +Accounts can be saved to either a PEM file or a keystore file. +While PEM wallets are less secure for storing secret keys, they are convenient for testing purposes. +Keystore files offer a higher level of security. - // #### Saving the Account to a PEM File - // ```js +#### Saving the Account to a PEM File +```js { const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); @@ -128,10 +127,10 @@ const account = new Account(secretKey); account.saveToPem(path.resolve("wallet.pem")); } - // ``` +``` - // #### Saving the Account to a Keystore File - // ```js +#### Saving the Account to a Keystore File +```js { const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); @@ -140,73 +139,63 @@ account.saveToKeystore(path.resolve("keystoreWallet.json"), "password"); } - // ``` +``` - // ### Using a Ledger Device +### Using a Ledger Device - // You can manage your account with a Ledger device, allowing you to sign both transactions and messages while keeping your keys secure. +You can manage your account with a Ledger device, allowing you to sign both transactions and messages while keeping your keys secure. - // Note: **The multiversx-sdk package does not include Ledger support by default. To enable it, install the package with Ledger dependencies**: +Note: **The multiversx-sdk package does not include Ledger support by default. To enable it, install the package with Ledger dependencies**: ```bash npm install @multiversx/sdk-hw-provider ``` - // #### Creating a Ledger Account - // This can be done using the dedicated library. You can find more information [here](/sdk-and-tools/sdk-js/sdk-js-signing-providers/#the-hardware-wallet-provider). +#### Creating a Ledger Account +This can be done using the dedicated library. You can find more information [here](/sdk-and-tools/sdk-js/sdk-js-signing-providers/#the-hardware-wallet-provider). - // When signing transactions or messages, the Ledger device will prompt you to confirm the details before proceeding. +When signing transactions or messages, the Ledger device will prompt you to confirm the details before proceeding. - // ### Compatibility with IAccount Interface +### Compatibility with IAccount Interface - // The `Account` implements the `IAccount` interface, making it compatible with transaction controllers and any other component that expects this interface. -})().catch((e) => { - console.log({ e }); -}); +The `Account` implements the `IAccount` interface, making it compatible with transaction controllers and any other component that expects this interface. -import { - Address, - ApiNetworkProvider, - DevnetEntrypoint, - ProxyNetworkProvider, - SmartContractQuery, - Token, - Transaction, -(async () => { - // ## Calling the Faucet +md-start - // This functionality is not yet available through the entrypoint, but we recommend using the faucet available within the Web Wallet. For more details about the faucet [see this](/wallet/web-wallet/#testnet-and-devnet-faucet). +## Calling the Faucet - // - [Testnet Wallet](https://testnet-wallet.multiversx.com/). - // - [Devnet Wallet](https://devnet-wallet.multiversx.com/). +This functionality is not yet available through the entrypoint, but we recommend using the faucet available within the Web Wallet. For more details about the faucet [see this](/wallet/web-wallet/#testnet-and-devnet-faucet). - // ### Interacting with the network +- [Testnet Wallet](https://testnet-wallet.multiversx.com/). +- [Devnet Wallet](https://devnet-wallet.multiversx.com/). - // The entrypoint exposes a few ways to directly interact with the network, such as: +### Interacting with the network - // - `recallAccountNonce(address: Address): Promise;` - // - `sendTransactions(transactions: Transaction[]): Promise<[number, string[]]>;` - // - `sendTransaction(transaction: Transaction): Promise;` - // - `getTransaction(txHash: string): Promise;` - // - `awaitCompletedTransaction(txHash: string): Promise;` +The entrypoint exposes a few ways to directly interact with the network, such as: - // Some other methods are exposed through a so called **network provider**. +- `recallAccountNonce(address: Address): Promise;` +- `sendTransactions(transactions: Transaction[]): Promise<[number, string[]]>;` +- `sendTransaction(transaction: Transaction): Promise;` +- `getTransaction(txHash: string): Promise;` +- `awaitCompletedTransaction(txHash: string): Promise;` - // - **ApiNetworkProvider**: Interacts with the API, which is a layer over the proxy. It fetches data from the network and `Elastic Search`. - // - **ProxyNetworkProvider**: Interacts directly with the proxy of an observing squad. +Some other methods are exposed through a so called **network provider**. - // To get the underlying network provider from our entrypoint, we can do as follows: +- **ApiNetworkProvider**: Interacts with the API, which is a layer over the proxy. It fetches data from the network and `Elastic Search`. +- **ProxyNetworkProvider**: Interacts directly with the proxy of an observing squad. - // ```js +To get the underlying network provider from our entrypoint, we can do as follows: + +```js { const entrypoint = new DevnetEntrypoint(); const networkProvider = entrypoint.createNetworkProvider(); } - // ``` +``` - // ### Creating a network provider - // When manually instantiating a network provider, you can provide a configuration to specify the client name and set custom request options. +### Creating a network provider +When manually instantiating a network provider, you can provide a configuration to specify the client name and set custom request options. - // ```js +```js { // Create a configuration object const config = { @@ -223,30 +212,30 @@ import { // Instantiate the network provider with the config const api = new ApiNetworkProvider("https://devnet-api.multiversx.com", config); } - // ``` +``` - // A full list of available methods for `ApiNetworkProvider` can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/classes/ApiNetworkProvider.html). +A full list of available methods for `ApiNetworkProvider` can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/classes/ApiNetworkProvider.html). - // Both `ApiNetworkProvider` and `ProxyNetworkProvider` implement a common interface, which can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/interfaces/INetworkProvider.html). This allows them to be used interchangeably. +Both `ApiNetworkProvider` and `ProxyNetworkProvider` implement a common interface, which can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/interfaces/INetworkProvider.html). This allows them to be used interchangeably. - // The classes returned by the API expose the most commonly used fields directly for convenience. However, each object also contains a `raw` field that stores the original API response, allowing access to additional fields if needed. +The classes returned by the API expose the most commonly used fields directly for convenience. However, each object also contains a `raw` field that stores the original API response, allowing access to additional fields if needed. - // ## Fetching data from the network +## Fetching data from the network - // ### Fetching the network config +### Fetching the network config - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const networkProvider = entrypoint.createNetworkProvider(); const networkConfig = networkProvider.getNetworkConfig(); } - // ``` +``` - // ### Fetching the network status +### Fetching the network status - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const networkProvider = entrypoint.createNetworkProvider(); @@ -254,96 +243,96 @@ import { const metaNetworkStatus = networkProvider.getNetworkStatus(); // fetches status from metachain const networkStatus = networkProvider.getNetworkStatus(); // fetches status from shard one } - // ``` +``` - // ### Fetching a Block from the Network - // To fetch a block, we first instantiate the required arguments and use its hash. The API only supports fetching blocks by hash, whereas the **PROXY** allows fetching blocks by either hash or nonce. +### Fetching a Block from the Network +To fetch a block, we first instantiate the required arguments and use its hash. The API only supports fetching blocks by hash, whereas the **PROXY** allows fetching blocks by either hash or nonce. - // When using the **PROXY**, keep in mind that the shard must also be specified in the arguments. +When using the **PROXY**, keep in mind that the shard must also be specified in the arguments. - // #### Fetching a block using the **API** - // ```js +#### Fetching a block using the **API** +```js { const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; const block = await api.getBlock(blockHash); } - // ``` +``` - // Additionally, we can fetch the latest block from the network: +Additionally, we can fetch the latest block from the network: - // ```js +```js { const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); const latestBlock = await api.getLatestBlock(); } - // ``` +``` - // #### Fetching a block using the **PROXY** +#### Fetching a block using the **PROXY** - // When using the proxy, we have to provide the shard, as well. - // ```js +When using the proxy, we have to provide the shard, as well. +```js { const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; const block = proxy.getBlock({ blockHash, shard: 1 }); } - // ``` +``` - // We can also fetch the latest block from the network. - // By default, the shard will be the metachain, but we can specify a different shard if needed. +We can also fetch the latest block from the network. +By default, the shard will be the metachain, but we can specify a different shard if needed. - // ```js +```js { const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); const latestBlock = proxy.getLatestBlock(); } - // ``` +``` - // ### Fetching an Account - // To fetch an account, we need its address. Once we have the address, we create an `Address` object and pass it as an argument to the method. +### Fetching an Account +To fetch an account, we need its address. Once we have the address, we create an `Address` object and pass it as an argument to the method. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); const account = await api.getAccount(alice); } - // ``` +``` - // ### Fetching an Account's Storage - // We can also fetch an account's storage, allowing us to retrieve all key-value pairs saved for that account. +### Fetching an Account's Storage +We can also fetch an account's storage, allowing us to retrieve all key-value pairs saved for that account. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); const account = await api.getAccountStorage(alice); } - // ``` +``` - // If we only want to fetch a specific key, we can do so as follows: +If we only want to fetch a specific key, we can do so as follows: - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); const account = await api.getAccountStorageEntry(alice, "testKey"); } - // ``` +``` - // ### Waiting for an Account to Meet a Condition - // There are times when we need to wait for a specific condition to be met before proceeding with an action. - // For example, let's say we want to send 7 EGLD from Alice to Bob, but this can only happen once Alice's balance reaches at least 7 EGLD. - // This approach is useful in scenarios where you're waiting for external funds to be sent to Alice, enabling her to transfer the required amount to another recipient. +### Waiting for an Account to Meet a Condition +There are times when we need to wait for a specific condition to be met before proceeding with an action. +For example, let's say we want to send 7 EGLD from Alice to Bob, but this can only happen once Alice's balance reaches at least 7 EGLD. +This approach is useful in scenarios where you're waiting for external funds to be sent to Alice, enabling her to transfer the required amount to another recipient. - // To implement this, we need to define the condition to check each time the account is fetched from the network. We create a function that takes an `AccountOnNetwork` object as an argument and returns a `bool`. - // Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. +To implement this, we need to define the condition to check each time the account is fetched from the network. We create a function that takes an `AccountOnNetwork` object as an argument and returns a `bool`. +Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -354,14 +343,14 @@ import { const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); const account = await api.awaitAccountOnCondition(alice, condition); } - // ``` +``` - // ### Sending and Simulating Transactions - // To execute transactions, we use the network providers to broadcast them to the network. Keep in mind that for transactions to be processed, they must be signed. +### Sending and Simulating Transactions +To execute transactions, we use the network providers to broadcast them to the network. Keep in mind that for transactions to be processed, they must be signed. - // #### Sending a Transaction +#### Sending a Transaction - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -380,10 +369,10 @@ import { const transactionHash = await api.sendTransaction(transaction); } - // ``` +``` - // #### Sending multiple transactions - // ```js +#### Sending multiple transactions +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -424,12 +413,12 @@ import { thirdTransaction, ]); } - // ``` +``` - // #### Simulating transactions - // A transaction can be simulated before being sent for processing by the network. This is primarily used for smart contract calls, allowing you to preview the results produced by the smart contract. +#### Simulating transactions +A transaction can be simulated before being sent for processing by the network. This is primarily used for smart contract calls, allowing you to preview the results produced by the smart contract. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -447,12 +436,12 @@ import { const transactionOnNetwork = await api.simulateTransaction(transaction); } - // ``` +``` - // #### Estimating the gas cost of a transaction - // Before sending a transaction to the network for processing, you can retrieve the estimated gas limit required for the transaction to be executed. +#### Estimating the gas cost of a transaction +Before sending a transaction to the network for processing, you can retrieve the estimated gas limit required for the transaction to be executed. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -473,12 +462,12 @@ import { const transactionCostResponse = await api.estimateTransactionCost(transaction); } - // ``` +``` - // ### Waiting for transaction completion - // After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. +### Waiting for transaction completion +After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -486,12 +475,12 @@ import { const txHash = "exampletransactionhash"; const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); } - // ``` +``` - // ### Waiting for a Transaction to Satisfy a Condition - // Similar to accounts, we can wait until a transaction meets a specific condition. +### Waiting for a Transaction to Satisfy a Condition +Similar to accounts, we can wait until a transaction meets a specific condition. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -501,12 +490,12 @@ import { const txHash = "exampletransactionhash"; const transactionOnNetwork = await api.awaitTransactionOnCondition(txHash, condition); } - // ``` +``` - // ### Waiting for transaction completion - // After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. +### Waiting for transaction completion +After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -514,12 +503,12 @@ import { const txHash = "exampletransactionhash"; const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); } - // ``` +``` - // ### Fetching Transactions from the Network - // After sending a transaction, we can fetch it from the network using the transaction hash, which we receive after broadcasting the transaction. +### Fetching Transactions from the Network +After sending a transaction, we can fetch it from the network using the transaction hash, which we receive after broadcasting the transaction. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -527,12 +516,12 @@ import { const txHash = "exampletransactionhash"; const transactionOnNetwork = await api.getTransaction(txHash); } - // ``` +``` - // ### Fetching a token from an account - // We can fetch a specific token (ESDT, MetaESDT, SFT, NFT) from an account by providing the account's address and the token identifier. +### Fetching a token from an account +We can fetch a specific token (ESDT, MetaESDT, SFT, NFT) from an account by providing the account's address and the token identifier. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -544,12 +533,12 @@ import { token = new Token({ identifier: "NFT-987654", nonce: 11n }); // NFT tokenOnNetwork = await api.getTokenOfAccount(alice, token); } - // ``` +``` - // ### Fetching all fungible tokens of an account - // Fetches all fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. +### Fetching all fungible tokens of an account +Fetches all fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -557,12 +546,12 @@ import { const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); const fungibleTokens = await api.getFungibleTokensOfAccount(alice); } - // ``` +``` - // ### Fetching all non-fungible tokens of an account - // Fetches all non-fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. +### Fetching all non-fungible tokens of an account +Fetches all non-fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -570,12 +559,12 @@ import { const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); const nfts = await api.getNonFungibleTokensOfAccount(alice); } - // ``` +``` - // ### Fetching token metadata - // If we want to fetch the metadata of a token (e.g., owner, decimals, etc.), we can use the following methods: +### Fetching token metadata +If we want to fetch the metadata of a token (e.g., owner, decimals, etc.), we can use the following methods: - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -586,12 +575,12 @@ import { // used for METAESDT, SFT, NFT const nonFungibleTokenDefinition = await api.getDefinitionOfTokenCollection("NFTEST-ec88b8"); } - // ``` +``` - // ### Querying Smart Contracts - // Smart contract queries, or view functions, are endpoints that only read data from the contract. To send a query to the observer nodes, we can proceed as follows: +### Querying Smart Contracts +Smart contract queries, or view functions, are endpoints that only read data from the contract. To send a query to the observer nodes, we can proceed as follows: - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -603,13 +592,13 @@ import { }); const response = await api.queryContract(query); } - // ``` +``` - // ### Custom Api/Proxy calls - // The methods exposed by the `ApiNetworkProvider` or `ProxyNetworkProvider` are the most common and widely used. However, there may be times when custom API calls are needed. For these cases, we’ve created generic methods for both GET and POST requests. - // Let’s assume we want to retrieve all the transactions sent by Alice in which the `delegate` function was called. +### Custom Api/Proxy calls +The methods exposed by the `ApiNetworkProvider` or `ProxyNetworkProvider` are the most common and widely used. However, there may be times when custom API calls are needed. For these cases, we’ve created generic methods for both GET and POST requests. +Let’s assume we want to retrieve all the transactions sent by Alice in which the `delegate` function was called. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const api = entrypoint.createNetworkProvider(); @@ -619,36 +608,25 @@ import { const response = await api.doGetGeneric(url); } - // ``` -})().catch((e) => { - console.log({ e }); -}); +``` -import { - Account, - Address, - DevnetEntrypoint, - Token, - TokenTransfer, - TransactionsFactoryConfig, - TransfersController, - TransferTransactionsFactory, -(async () => { - // ## Creating transactions +md-start - // In this section, we’ll explore how to create different types of transactions. To create transactions, we can use either controllers or factories. - // Controllers are ideal for quick scripts or network interactions, while factories provide a more granular and lower-level approach, typically required for DApps. +## Creating transactions - // Controllers typically use the same parameters as factories, but they also require an Account object and the sender’s nonce. - // Controllers also include extra functionality, such as waiting for transaction completion and parsing transactions. - // The same functionality can be achieved for transactions built using factories, and we’ll see how in the sections below. In the next section, we’ll learn how to create transactions using both methods. +In this section, we’ll explore how to create different types of transactions. To create transactions, we can use either controllers or factories. +Controllers are ideal for quick scripts or network interactions, while factories provide a more granular and lower-level approach, typically required for DApps. - // ### Instantiating Controllers and Factories - // There are two ways to create controllers and factories: - // 1. Get them from the entrypoint. - // 2. Manually instantiate them. +Controllers typically use the same parameters as factories, but they also require an Account object and the sender’s nonce. +Controllers also include extra functionality, such as waiting for transaction completion and parsing transactions. +The same functionality can be achieved for transactions built using factories, and we’ll see how in the sections below. In the next section, we’ll learn how to create transactions using both methods. - // ```js +### Instantiating Controllers and Factories +There are two ways to create controllers and factories: +1. Get them from the entrypoint. +2. Manually instantiate them. + +```js { const entrypoint = new DevnetEntrypoint(); @@ -662,14 +640,14 @@ import { const config = new TransactionsFactoryConfig({ chainID: "D" }); const factory = new TransferTransactionsFactory({ config }); } - // ``` +``` - // ### Token transfers - // We can send both native tokens (EGLD) and ESDT tokens using either the controller or the factory. - // #### Native Token Transfers Using the Controller - // When using the controller, the transaction will be signed because we’ll be working with an Account. +### Token transfers +We can send both native tokens (EGLD) and ESDT tokens using either the controller or the factory. +#### Native Token Transfers Using the Controller +When using the controller, the transaction will be signed because we’ll be working with an Account. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); @@ -692,15 +670,15 @@ import { const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. +If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. - // #### Native Token Transfers Using the Factory - // When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. - // You will need to handle these aspects after the transaction is created. +#### Native Token Transfers Using the Factory +When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. +You will need to handle these aspects after the transaction is created. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const factory = entrypoint.createTransfersTransactionsFactory(); @@ -726,13 +704,13 @@ import { const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. +If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. - // #### Custom token transfers using the controller +#### Custom token transfers using the controller - // ```js +```js { const entrypoint = new DevnetEntrypoint(); @@ -764,14 +742,14 @@ import { const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. +If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. - // #### Custom token transfers using the factory - // When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. These aspects should be handled after the transaction is created. +#### Custom token transfers using the factory +When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. These aspects should be handled after the transaction is created. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const factory = entrypoint.createTransfersTransactionsFactory(); @@ -805,15 +783,15 @@ import { const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. +If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. - // #### Sending native and custom tokens - // Both native and custom tokens can now be sent. If a `nativeAmount` is provided along with `tokenTransfers`, the native token will be included in the `MultiESDTNFTTransfer` built-in function call. - // We can send both types of tokens using either the `controller` or the `factory`, but for simplicity, we’ll use the controller in this example. +#### Sending native and custom tokens +Both native and custom tokens can now be sent. If a `nativeAmount` is provided along with `tokenTransfers`, the native token will be included in the `MultiESDTNFTTransfer` built-in function call. +We can send both types of tokens using either the `controller` or the `factory`, but for simplicity, we’ll use the controller in this example. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); @@ -843,63 +821,42 @@ import { const txHash = await entrypoint.sendTransaction(transaction); } - // ``` -})().catch((e) => { - console.log({ e }); -}); - -import { - Abi, - Account, - Address, - AddressComputer, - BigUIntValue, - BinaryCodec, - DevnetEntrypoint, - Field, - gatherAllEvents, - SmartContractTransactionsOutcomeParser, - Struct, - Token, - TokenIdentifierValue, - TokenTransfer, - TransactionEventsParser, - U32Value, - U64Value, -import { loadAbiRegistry } from "../src/testutils"; -(async () => { - // ### Smart Contracts - - // #### Contract ABIs - - // A contract's ABI (Application Binary Interface) describes the endpoints, data structures, and events that the contract exposes. - // While interactions with the contract are possible without the ABI, they are much easier to implement when the definitions are available. - - // #### Loading the ABI from a file - // ```js +``` + +md-start + +### Smart Contracts + +#### Contract ABIs + +A contract's ABI (Application Binary Interface) describes the endpoints, data structures, and events that the contract exposes. +While interactions with the contract are possible without the ABI, they are much easier to implement when the definitions are available. + +#### Loading the ABI from a file +```js { let abiJson = await promises.readFile("../src/testData/adder.abi.json", { encoding: "utf8" }); let abiObj = JSON.parse(abiJson); let abi = Abi.create(abiObj); } - // ``` +``` - // #### Loading the ABI from an URL +#### Loading the ABI from an URL - // ```js +```js { const response = await axios.get( "https://github.com/multiversx/mx-sdk-js-core/raw/main/src/testdata/adder.abi.json", ); let abi = Abi.create(response.data); } - // ``` +``` - // #### Manually construct the ABI +#### Manually construct the ABI - // If an ABI file isn’t available, but you know the contract’s endpoints and data types, you can manually construct the ABI. +If an ABI file isn’t available, but you know the contract’s endpoints and data types, you can manually construct the ABI. - // ```js +```js { let abi = Abi.create({ endpoints: [ @@ -911,9 +868,9 @@ import { loadAbiRegistry } from "../src/testutils"; ], }); } - // ``` +``` - // ```js +```js { let abi = Abi.create({ endpoints: [ @@ -930,16 +887,16 @@ import { loadAbiRegistry } from "../src/testutils"; ], }); } - // ``` +``` - // ### Smart Contract deployments - // For creating smart contract deployment transactions, we have two options: a controller and a factory. Both function similarly to the ones used for token transfers. - // When creating transactions that interact with smart contracts, it's recommended to provide the ABI file to the controller or factory if possible. - // This allows arguments to be passed as native Javascript values. If the ABI is not available, but we know the expected data types, we can pass arguments as typed values (e.g., `BigUIntValue`, `ListValue`, `StructValue`, etc.) or as raw bytes. +### Smart Contract deployments +For creating smart contract deployment transactions, we have two options: a controller and a factory. Both function similarly to the ones used for token transfers. +When creating transactions that interact with smart contracts, it's recommended to provide the ABI file to the controller or factory if possible. +This allows arguments to be passed as native Javascript values. If the ABI is not available, but we know the expected data types, we can pass arguments as typed values (e.g., `BigUIntValue`, `ListValue`, `StructValue`, etc.) or as raw bytes. - // #### Deploying a Smart Contract Using the Controller +#### Deploying a Smart Contract Using the Controller - // ```js +```js { const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); const sender = await Account.newFromPem(filePath); @@ -969,22 +926,22 @@ import { loadAbiRegistry } from "../src/testutils"; // broadcasting the transaction const txHash = await entrypoint.sendTransaction(deployTransaction); } - // ``` +``` - // :::tip - // When creating transactions using [`SmartContractController`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/SmartContractController.html) or [`SmartContractTransactionsFactory`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/SmartContractTransactionsFactory.html), even if the ABI is available and provided, - // you can still use [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/TypedValue.html) objects as arguments for deployments and interactions. +:::tip +When creating transactions using [`SmartContractController`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/SmartContractController.html) or [`SmartContractTransactionsFactory`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/SmartContractTransactionsFactory.html), even if the ABI is available and provided, +you can still use [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/TypedValue.html) objects as arguments for deployments and interactions. - // Even further, you can use a mix of [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/TypedValue.html) objects and plain JavaScript values and objects. For example: +Even further, you can use a mix of [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/TypedValue.html) objects and plain JavaScript values and objects. For example: - // ```js - // let args = [new U32Value(42), "hello", { foo: "bar" }, new TokenIdentifierValue("TEST-abcdef")]; - // ``` - // ::: +```js +let args = [new U32Value(42), "hello", { foo: "bar" }, new TokenIdentifierValue("TEST-abcdef")]; +``` +::: - // #### Parsing contract deployment transactions +#### Parsing contract deployment transactions - // ```js +```js { // We use the transaction hash we got when broadcasting the transaction @@ -994,11 +951,11 @@ import { loadAbiRegistry } from "../src/testutils"; const outcome = await controller.awaitCompletedDeploy("txHash"); // waits for transaction completion and parses the result const contractAddress = outcome.contracts[0].address; } - // ``` +``` - // If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: +If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: - // ```js +```js { // We use the transaction hash we got when broadcasting the transaction // If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: @@ -1011,13 +968,13 @@ import { loadAbiRegistry } from "../src/testutils"; // parsing the transaction const outcome = await controller.parseDeploy(transactionOnNetwork); } - // ``` +``` - // #### Computing the contract address +#### Computing the contract address - // Even before broadcasting, at the moment you know the sender's address and the nonce for your deployment transaction, you can (deterministically) compute the (upcoming) address of the smart contract: +Even before broadcasting, at the moment you know the sender's address and the nonce for your deployment transaction, you can (deterministically) compute the (upcoming) address of the smart contract: - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const factory = entrypoint.createSmartContractTransactionsFactory(); @@ -1043,12 +1000,12 @@ import { loadAbiRegistry } from "../src/testutils"; console.log("Contract address:", contractAddress.toBech32()); } - // ``` +``` - // #### Deploying a Smart Contract using the factory - // After the transaction is created the nonce needs to be properly set and the transaction should be signed before broadcasting it. +#### Deploying a Smart Contract using the factory +After the transaction is created the nonce needs to be properly set and the transaction should be signed before broadcasting it. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const factory = entrypoint.createSmartContractTransactionsFactory(); @@ -1092,15 +1049,15 @@ import { loadAbiRegistry } from "../src/testutils"; console.log(contractAddress.toBech32()); } - // ``` +``` - // ### Smart Contract calls +### Smart Contract calls - // In this section we'll see how we can call an endpoint of our previously deployed smart contract using both approaches with the `controller` and the `factory`. +In this section we'll see how we can call an endpoint of our previously deployed smart contract using both approaches with the `controller` and the `factory`. - // #### Calling a smart contract using the controller +#### Calling a smart contract using the controller - // ```js +```js { const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); const sender = await Account.newFromPem(filePath); @@ -1132,12 +1089,12 @@ import { loadAbiRegistry } from "../src/testutils"; console.log(txHash); } - // ``` +``` - // #### Parsing smart contract call transactions - // In our case, calling the add endpoint does not return anything, but similar to the example above, we could parse this transaction to get the output values of a smart contract call. +#### Parsing smart contract call transactions +In our case, calling the add endpoint does not return anything, but similar to the example above, we could parse this transaction to get the output values of a smart contract call. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const controller = entrypoint.createSmartContractController(); @@ -1146,13 +1103,13 @@ import { loadAbiRegistry } from "../src/testutils"; const parsedOutcome = await controller.awaitCompletedExecute(txHash); const values = parsedOutcome.values; } - // ``` +``` - // #### Calling a smart contract and sending tokens (transfer & execute) - // Additionally, if an endpoint requires a payment when called, we can send tokens to the contract while creating a smart contract call transaction. - // Both EGLD and ESDT tokens or a combination of both can be sent. This functionality is supported by both the controller and the factory. +#### Calling a smart contract and sending tokens (transfer & execute) +Additionally, if an endpoint requires a payment when called, we can send tokens to the contract while creating a smart contract call transaction. +Both EGLD and ESDT tokens or a combination of both can be sent. This functionality is supported by both the controller and the factory. - // ```js +```js { const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); const sender = await Account.newFromPem(filePath); @@ -1195,12 +1152,12 @@ import { loadAbiRegistry } from "../src/testutils"; console.log(txHash); } - // ``` +``` - // #### Calling a smart contract using the factory - // Let's create the same smart contract call transaction, but using the `factory`. +#### Calling a smart contract using the factory +Let's create the same smart contract call transaction, but using the `factory`. - // ```js +```js { const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); const alice = await Account.newFromPem(filePath); @@ -1243,12 +1200,12 @@ import { loadAbiRegistry } from "../src/testutils"; console.log(txHash); } - // ``` +``` - // #### Parsing transaction outcome - // As said before, the `add` endpoint we called does not return anything, but we could parse the outcome of smart contract call transactions, as follows: +#### Parsing transaction outcome +As said before, the `add` endpoint we called does not return anything, but we could parse the outcome of smart contract call transactions, as follows: - // ```js +```js { // load the abi file const entrypoint = new DevnetEntrypoint(); @@ -1258,16 +1215,16 @@ import { loadAbiRegistry } from "../src/testutils"; const transactionOnNetwork = await entrypoint.getTransaction(txHash); const outcome = parser.parseExecute({ transactionOnNetwork }); } - // ``` +``` - // #### Decoding transaction events - // You might be interested into decoding events emitted by a contract. You can do so by using the `TransactionEventsParser`. +#### Decoding transaction events +You might be interested into decoding events emitted by a contract. You can do so by using the `TransactionEventsParser`. - // Suppose we'd like to decode a `startPerformAction` event emitted by the [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract. +Suppose we'd like to decode a `startPerformAction` event emitted by the [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract. - // First, we load the abi file, then we fetch the transaction, we extract the event from the transaction and then we parse it. +First, we load the abi file, then we fetch the transaction, we extract the event from the transaction and then we parse it. - // ```js +```js { // load the abi files const entrypoint = new DevnetEntrypoint(); @@ -1278,13 +1235,13 @@ import { loadAbiRegistry } from "../src/testutils"; const events = gatherAllEvents(transactionOnNetwork); const outcome = parser.parseEvents({ events }); } - // ``` +``` - // #### Decoding transaction events - // Whenever needed, the contract ABI can be used for manually encoding or decoding custom types. +#### Decoding transaction events +Whenever needed, the contract ABI can be used for manually encoding or decoding custom types. - // Let's encode a struct called EsdtTokenPayment (of [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract) into binary data. - // ```js +Let's encode a struct called EsdtTokenPayment (of [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract) into binary data. +```js { const abi = await loadAbiRegistry("../src/testdata/multisig-full.abi.json"); const paymentType = abi.getStruct("EsdtTokenPayment"); @@ -1300,10 +1257,10 @@ import { loadAbiRegistry } from "../src/testutils"; console.log(encoded.toString("hex")); } - // ``` +``` - // Now let's decode a struct using the ABI. - // ```js +Now let's decode a struct using the ABI. +```js { const abi = await loadAbiRegistry("../src/testdata/multisig-full.abi.json"); const actionStructType = abi.getEnum("Action"); @@ -1317,14 +1274,14 @@ import { loadAbiRegistry } from "../src/testutils"; const decodedValue = decoded.valueOf(); console.log(JSON.stringify(decodedValue, null, 4)); } - // ``` +``` - // ### Smart Contract queries - // When querying a smart contract, a **view function** is called. A view function does not modify the state of the contract, so we do not need to send a transaction. - // To perform this query, we use the **SmartContractController**. While we can use the contract's ABI file to encode the query arguments, we can also use it to parse the result. - // In this example, we will query the **adder smart contract** by calling its `getSum` endpoint. +### Smart Contract queries +When querying a smart contract, a **view function** is called. A view function does not modify the state of the contract, so we do not need to send a transaction. +To perform this query, we use the **SmartContractController**. While we can use the contract's ABI file to encode the query arguments, we can also use it to parse the result. +In this example, we will query the **adder smart contract** by calling its `getSum` endpoint. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); @@ -1336,12 +1293,12 @@ import { loadAbiRegistry } from "../src/testutils"; // creates the query, runs the query, parses the result const response = await controller.query({ contract: contractAddress, function: "getSum", arguments: [] }); } - // ``` +``` - // If we need more granular control, we can split the process into three steps: **create the query, run the query, and parse the query response**. - // This approach achieves the same result as the previous example. +If we need more granular control, we can split the process into three steps: **create the query, run the query, and parse the query response**. +This approach achieves the same result as the previous example. - // ```js +```js { const entrypoint = new DevnetEntrypoint(); @@ -1362,14 +1319,14 @@ import { loadAbiRegistry } from "../src/testutils"; // parse the result const parsedResponse = controller.parseQueryResponse(response); } - // ``` +``` - // ### Upgrading a smart contract - // Contract upgrade transactions are similar to deployment transactions (see above) because they also require contract bytecode. - // However, in this case, the contract address is already known. Like deploying a smart contract, we can upgrade a smart contract using either the **controller** or the **factory**. +### Upgrading a smart contract +Contract upgrade transactions are similar to deployment transactions (see above) because they also require contract bytecode. +However, in this case, the contract address is already known. Like deploying a smart contract, we can upgrade a smart contract using either the **controller** or the **factory**. - // #### Uprgrading a smart contract using the controller - // ```js +#### Uprgrading a smart contract using the controller +```js { // prepare the account const entrypoint = new DevnetEntrypoint(); @@ -1410,24 +1367,22 @@ import { loadAbiRegistry } from "../src/testutils"; console.log({ txHash }); } - // ``` -})().catch((e) => { - console.log({ e }); -}); +``` + +md-start -(async () => { - // ### Token management +### Token management - // In this section, we're going to create transactions to issue fungible tokens, issue semi-fungible tokens, create NFTs, set token roles, but also parse these transactions to extract their outcome (e.g. get the token identifier of the newly issued token). +In this section, we're going to create transactions to issue fungible tokens, issue semi-fungible tokens, create NFTs, set token roles, but also parse these transactions to extract their outcome (e.g. get the token identifier of the newly issued token). - // These methods are available through the `TokenManagementController` and the `TokenManagementTransactionsFactory`. - // The controller also includes built-in methods for awaiting the completion of transactions and parsing their outcomes. - // For the factory, the same functionality can be achieved using the `TokenManagementTransactionsOutcomeParser`. +These methods are available through the `TokenManagementController` and the `TokenManagementTransactionsFactory`. +The controller also includes built-in methods for awaiting the completion of transactions and parsing their outcomes. +For the factory, the same functionality can be achieved using the `TokenManagementTransactionsOutcomeParser`. - // For scripts or quick network interactions, we recommend using the controller. However, for a more granular approach (e.g., DApps), the factory is the better choice. +For scripts or quick network interactions, we recommend using the controller. However, for a more granular approach (e.g., DApps), the factory is the better choice. - // #### Issuing fungible tokens using the controller - // ```js +#### Issuing fungible tokens using the controller +```js { // create the entrypoint and the token management controller const entrypoint = new DevnetEntrypoint(); @@ -1461,10 +1416,10 @@ import { loadAbiRegistry } from "../src/testutils"; const tokenIdentifier = outcome[0].tokenIdentifier; } - // ``` +``` - // #### Issuing fungible tokens using the factory - // ```js +#### Issuing fungible tokens using the factory +```js { // create the entrypoint and the token management transactions factory const entrypoint = new DevnetEntrypoint(); @@ -1505,10 +1460,10 @@ import { loadAbiRegistry } from "../src/testutils"; const outcome = parser.parseIssueFungible(transactionOnNetwork); const tokenIdentifier = outcome[0].tokenIdentifier; } - // ``` +``` - // #### Setting special roles for fungible tokens using the controller - // ```js +#### Setting special roles for fungible tokens using the controller +```js { // create the entrypoint and the token management controller const entrypoint = new DevnetEntrypoint(); @@ -1544,10 +1499,10 @@ import { loadAbiRegistry } from "../src/testutils"; const roles = outcome[0].roles; const user = outcome[0].userAddress; } - // ``` +``` - // #### Setting special roles for fungible tokens using the factory - // ```js +#### Setting special roles for fungible tokens using the factory +```js { // create the entrypoint and the token management controller const entrypoint = new DevnetEntrypoint(); @@ -1589,10 +1544,10 @@ import { loadAbiRegistry } from "../src/testutils"; const roles = outcome[0].roles; const user = outcome[0].userAddress; } - // ``` +``` - // #### Issuing semi-fungible tokens using the controller - // ```js +#### Issuing semi-fungible tokens using the controller +```js { // create the entrypoint and the token management controller const entrypoint = new DevnetEntrypoint(); @@ -1629,10 +1584,10 @@ import { loadAbiRegistry } from "../src/testutils"; const tokenIdentifier = outcome[0].tokenIdentifier; } - // ``` +``` - // #### Issuing semi-fungible tokens using the factory - // ```js +#### Issuing semi-fungible tokens using the factory +```js { // create the entrypoint and the token management controller const entrypoint = new DevnetEntrypoint(); @@ -1672,11 +1627,11 @@ import { loadAbiRegistry } from "../src/testutils"; const tokenIdentifier = outcome[0].tokenIdentifier; } - // ``` +``` - // #### Issuing NFT collection & creating NFTs using the controller +#### Issuing NFT collection & creating NFTs using the controller - // ```js +```js { // create the entrypoint and the token management controller const entrypoint = new DevnetEntrypoint(); @@ -1734,10 +1689,10 @@ import { loadAbiRegistry } from "../src/testutils"; const nonce = outcomeNft[0].nonce; const initialQuantity = outcomeNft[0].initialQuantity; } - // ``` +``` - // #### Issuing NFT collection & creating NFTs using the factory - // ```js +#### Issuing NFT collection & creating NFTs using the factory +```js { // create the entrypoint and the token management transdactions factory const entrypoint = new DevnetEntrypoint(); @@ -1802,28 +1757,26 @@ import { loadAbiRegistry } from "../src/testutils"; const identifier = outcome[0].tokenIdentifier; } - // ``` +``` + +These are just a few examples of what you can do using the token management controller or factory. For a complete list of supported methods, please refer to the autogenerated documentation: - // These are just a few examples of what you can do using the token management controller or factory. For a complete list of supported methods, please refer to the autogenerated documentation: +- [TokenManagementController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementController.html) +- [TokenManagementTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementTransactionsFactory.html) - // - [TokenManagementController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementController.html) - // - [TokenManagementTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementTransactionsFactory.html) -})().catch((e) => { - console.log({ e }); -}); +md-start -(async () => { - // ### Account management +### Account management - // The account management controller and factory allow us to create transactions for managing accounts, such as: - // - Guarding and unguarding accounts - // - Saving key-value pairs in the account storage, on the blockchain. +The account management controller and factory allow us to create transactions for managing accounts, such as: +- Guarding and unguarding accounts +- Saving key-value pairs in the account storage, on the blockchain. - // To learn more about Guardians, please refer to the [official documentation](/developers/built-in-functions/#setguardian). - // A guardian can also be set using the WebWallet, which leverages our hosted `Trusted Co-Signer Service`. Follow [this guide](/wallet/web-wallet/#guardian) for step-by-step instructions on guarding an account using the wallet. +To learn more about Guardians, please refer to the [official documentation](/developers/built-in-functions/#setguardian). +A guardian can also be set using the WebWallet, which leverages our hosted `Trusted Co-Signer Service`. Follow [this guide](/wallet/web-wallet/#guardian) for step-by-step instructions on guarding an account using the wallet. - // #### Guarding an account using the controller - // ```js +#### Guarding an account using the controller +```js { // create the entrypoint and the account controller const entrypoint = new DevnetEntrypoint(); @@ -1847,10 +1800,10 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Guarding an account using the factory - // ```js +#### Guarding an account using the factory +```js { // create the entrypoint and the account management factory const entrypoint = new DevnetEntrypoint(); @@ -1879,12 +1832,12 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // Once a guardian is set, we must wait **20 epochs** before it can be activated. After activation, all transactions sent from the account must also be signed by the guardian. +Once a guardian is set, we must wait **20 epochs** before it can be activated. After activation, all transactions sent from the account must also be signed by the guardian. - // #### Activating the guardian using the controller - // ```js +#### Activating the guardian using the controller +```js { // create the entrypoint and the account controller const entrypoint = new DevnetEntrypoint(); @@ -1906,10 +1859,10 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Activating the guardian using the factory - // ```js +#### Activating the guardian using the factory +```js { // create the entrypoint and the account factory const entrypoint = new DevnetEntrypoint(); @@ -1933,10 +1886,10 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Unguarding the account using the controller - // ```js +#### Unguarding the account using the controller +```js { // create the entrypoint and the account controller const entrypoint = new DevnetEntrypoint(); @@ -1958,10 +1911,10 @@ import { loadAbiRegistry } from "../src/testutils"; // the transaction should also be signed by the guardian before being sent otherwise it won't be executed const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Unguarding the guardian using the factory - // ```js +#### Unguarding the guardian using the factory +```js { // create the entrypoint and the account factory const entrypoint = new DevnetEntrypoint(); @@ -1985,12 +1938,12 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Saving a key-value pair to an account using the controller - // You can find more information [here](/developers/account-storage) regarding the account storage. +#### Saving a key-value pair to an account using the controller +You can find more information [here](/developers/account-storage) regarding the account storage. - // ```js +```js { // create the entrypoint and the account controller const entrypoint = new DevnetEntrypoint(); @@ -2012,10 +1965,10 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Saving a key-value pair to an account using the factory - // ```js +#### Saving a key-value pair to an account using the factory +```js { // create the entrypoint and the account factory const entrypoint = new DevnetEntrypoint(); @@ -2044,29 +1997,27 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` -})().catch((e) => { - console.log({ e }); -}); +``` + +md-start -(async () => { - // ### Delegation management +### Delegation management - // To learn more about staking providers and delegation, please refer to the official [documentation](/validators/delegation-manager/#introducing-staking-providers). - // In this section, we'll cover how to: - // - Create a new delegation contract - // - Retrieve the contract address - // - Delegate funds to the contract - // - Redelegate rewards - // - Claim rewards - // - Undelegate and withdraw funds +To learn more about staking providers and delegation, please refer to the official [documentation](/validators/delegation-manager/#introducing-staking-providers). +In this section, we'll cover how to: +- Create a new delegation contract +- Retrieve the contract address +- Delegate funds to the contract +- Redelegate rewards +- Claim rewards +- Undelegate and withdraw funds - // These operations can be performed using both the controller and the **factory**. For a complete list of supported methods, please refer to the autogenerated documentation: - // - [DelegationController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationController.html) - // - [DelegationTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationTransactionsFactory.html) +These operations can be performed using both the controller and the **factory**. For a complete list of supported methods, please refer to the autogenerated documentation: +- [DelegationController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationController.html) +- [DelegationTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/DelegationTransactionsFactory.html) - // #### Creating a New Delegation Contract Using the Controller - // ```js +#### Creating a New Delegation Contract Using the Controller +```js { // create the entrypoint and the delegation controller const entrypoint = new DevnetEntrypoint(); @@ -2096,10 +2047,10 @@ import { loadAbiRegistry } from "../src/testutils"; const contractAddress = outcome[0].contractAddress; } - // ``` +``` - // #### Creating a new delegation contract using the factory - // ```js +#### Creating a new delegation contract using the factory +```js { // create the entrypoint and the delegation factory const entrypoint = new DevnetEntrypoint(); @@ -2133,12 +2084,12 @@ import { loadAbiRegistry } from "../src/testutils"; const outcome = parser.parseIssueFungible(transactionOnNetwork); const tokenIdentifier = outcome[0].tokenIdentifier; } - // ``` +``` - // #### Delegating funds to the contract using the Controller - // We can send funds to a delegation contract to earn rewards. +#### Delegating funds to the contract using the Controller +We can send funds to a delegation contract to earn rewards. - // ```js +```js { // create the entrypoint and the delegation controller const entrypoint = new DevnetEntrypoint(); @@ -2160,10 +2111,10 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Delegating funds to the contract using the factory - // ```js +#### Delegating funds to the contract using the factory +```js { // create the entrypoint and the delegation factory const entrypoint = new DevnetEntrypoint(); @@ -2190,12 +2141,12 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Redelegating rewards using the Controller - // Over time, as rewards accumulate, we may choose to redelegate them to the contract to maximize earnings. +#### Redelegating rewards using the Controller +Over time, as rewards accumulate, we may choose to redelegate them to the contract to maximize earnings. - // ```js +```js { // create the entrypoint and the delegation controller const entrypoint = new DevnetEntrypoint(); @@ -2219,10 +2170,10 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Redelegating rewards using the factory - // ```js +#### Redelegating rewards using the factory +```js { // create the entrypoint and the delegation factory const entrypoint = new DevnetEntrypoint(); @@ -2248,12 +2199,12 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Claiming rewards using the Controller - // We can also claim our rewards when needed. +#### Claiming rewards using the Controller +We can also claim our rewards when needed. - // ```js +```js { // create the entrypoint and the delegation controller const entrypoint = new DevnetEntrypoint(); @@ -2273,10 +2224,10 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Claiming rewards using the factory - // ```js +#### Claiming rewards using the factory +```js { // create the entrypoint and the delegation factory const entrypoint = new DevnetEntrypoint(); @@ -2302,12 +2253,12 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Undelegating funds using the Controller - // By **undelegating**, we signal the contract that we want to retrieve our staked funds. This process requires a **10-epoch unbonding period** before the funds become available. +#### Undelegating funds using the Controller +By **undelegating**, we signal the contract that we want to retrieve our staked funds. This process requires a **10-epoch unbonding period** before the funds become available. - // ```js +```js { // create the entrypoint and the delegation controller const entrypoint = new DevnetEntrypoint(); @@ -2328,10 +2279,10 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Undelegating funds using the factory - // ```js +#### Undelegating funds using the factory +```js { // create the entrypoint and the delegation factory const entrypoint = new DevnetEntrypoint(); @@ -2358,12 +2309,12 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Withdrawing funds using the Controller - // After the `10-epoch unbonding period` is complete, we can proceed with withdrawing our staked funds using the controller. This final step allows us to regain access to the previously delegated funds. +#### Withdrawing funds using the Controller +After the `10-epoch unbonding period` is complete, we can proceed with withdrawing our staked funds using the controller. This final step allows us to regain access to the previously delegated funds. - // ```js +```js { // create the entrypoint and the delegation controller const entrypoint = new DevnetEntrypoint(); @@ -2384,10 +2335,10 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Withdrawing funds using the factory - // ```js +#### Withdrawing funds using the factory +```js { // create the entrypoint and the delegation factory const entrypoint = new DevnetEntrypoint(); @@ -2413,28 +2364,26 @@ import { loadAbiRegistry } from "../src/testutils"; // sending the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` -})().catch((e) => { - console.log({ e }); -}); +``` + +md-start -(async () => { - // ### Relayed transactions - // We are currently on the `third iteration (V3)` of relayed transactions. V1 and V2 will soon be deactivated, so we will focus on V3. +### Relayed transactions +We are currently on the `third iteration (V3)` of relayed transactions. V1 and V2 will soon be deactivated, so we will focus on V3. - // For V3, two new fields have been added to transactions: - // - relayer - // - relayerSignature +For V3, two new fields have been added to transactions: +- relayer +- relayerSignature - // Signing Process: - // 1. The relayer must be set before the sender signs the transaction. - // 2. Once the sender has signed, the relayer can also sign the transaction and broadcast it. +Signing Process: +1. The relayer must be set before the sender signs the transaction. +2. Once the sender has signed, the relayer can also sign the transaction and broadcast it. - // **Important Consideration**: - // Relayed V3 transactions require an additional `50,000` gas. - // Let’s see how to create a relayed transaction: +**Important Consideration**: +Relayed V3 transactions require an additional `50,000` gas. +Let’s see how to create a relayed transaction: - // ```js +```js { const entrypoint = new DevnetEntrypoint(); const walletsPath = path.join("../src", "testdata", "testwallets"); @@ -2464,15 +2413,15 @@ import { loadAbiRegistry } from "../src/testutils"; // broadcast the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Creating relayed transactions using controllers - // We can create relayed transactions using any of the available controllers. - // Each controller includes a relayer argument, which must be set if we want to create a relayed transaction. +#### Creating relayed transactions using controllers +We can create relayed transactions using any of the available controllers. +Each controller includes a relayer argument, which must be set if we want to create a relayed transaction. - // Let’s issue a fungible token using a relayed transaction: +Let’s issue a fungible token using a relayed transaction: - // ```js +```js { // create the entrypoint and the token management controller const entrypoint = new DevnetEntrypoint(); @@ -2508,15 +2457,15 @@ import { loadAbiRegistry } from "../src/testutils"; // broadcast the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Creating relayed transactions using factories - // Unlike controllers, `transaction factories` do not have a `relayer` argument. Instead, the **relayer must be set after creating the transaction**. - // This approach is beneficial because the **transaction is not signed by the sender at the time of creation**, allowing flexibility in setting the relayer before signing. +#### Creating relayed transactions using factories +Unlike controllers, `transaction factories` do not have a `relayer` argument. Instead, the **relayer must be set after creating the transaction**. +This approach is beneficial because the **transaction is not signed by the sender at the time of creation**, allowing flexibility in setting the relayer before signing. - // Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: +Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: - // ```js +```js { // create the entrypoint and the token management factory const entrypoint = new DevnetEntrypoint(); @@ -2558,31 +2507,29 @@ import { loadAbiRegistry } from "../src/testutils"; // broadcast the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` -})().catch((e) => { - console.log({ e }); -}); +``` + +md-start -(async () => { - // ### Guarded transactions - // Similar to relayers, transactions also have two additional fields: +### Guarded transactions +Similar to relayers, transactions also have two additional fields: - // - guardian - // - guardianSignature +- guardian +- guardianSignature - // Each controller includes an argument for the guardian. The transaction can either: - // 1. Be sent to a service that signs it using the guardian’s account, or - // 2. Be signed by another account acting as a guardian. +Each controller includes an argument for the guardian. The transaction can either: +1. Be sent to a service that signs it using the guardian’s account, or +2. Be signed by another account acting as a guardian. - // Let’s issue a token using a guarded account: +Let’s issue a token using a guarded account: - // #### Creating guarded transactions using controllers - // We can create guarded transactions using any of the available controllers. +#### Creating guarded transactions using controllers +We can create guarded transactions using any of the available controllers. - // Each controller method includes a guardian argument, which must be set if we want to create a guarded transaction. - // Let’s issue a fungible token using a relayed transaction: +Each controller method includes a guardian argument, which must be set if we want to create a guarded transaction. +Let’s issue a fungible token using a relayed transaction: - // ```js +```js { // create the entrypoint and the token management controller const entrypoint = new DevnetEntrypoint(); @@ -2618,15 +2565,15 @@ import { loadAbiRegistry } from "../src/testutils"; // broadcast the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` - // #### Creating guarded transactions using factories - // Unlike controllers, `transaction factories` do not have a `guardian` argument. Instead, the **guardian must be set after creating the transaction**. - // This approach is beneficial because the transaction is **not signed by the sender at the time of creation**, allowing flexibility in setting the guardian before signing. +#### Creating guarded transactions using factories +Unlike controllers, `transaction factories` do not have a `guardian` argument. Instead, the **guardian must be set after creating the transaction**. +This approach is beneficial because the transaction is **not signed by the sender at the time of creation**, allowing flexibility in setting the guardian before signing. - // Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: +Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: - // ```js +```js { // create the entrypoint and the token management factory const entrypoint = new DevnetEntrypoint(); @@ -2668,33 +2615,31 @@ import { loadAbiRegistry } from "../src/testutils"; // broadcast the transaction const txHash = await entrypoint.sendTransaction(transaction); } - // ``` +``` + +We can create guarded relayed transactions just like we did before. However, keep in mind: - // We can create guarded relayed transactions just like we did before. However, keep in mind: +Only the sender can be guarded, the relayer cannot be guarded. - // Only the sender can be guarded, the relayer cannot be guarded. +Flow for Creating Guarded Relayed Transactions: +- Using Controllers: +1. Set both guardian and relayer fields. +2. The transaction must be signed by both the guardian and the relayer. +- Using Factories: - // Flow for Creating Guarded Relayed Transactions: - // - Using Controllers: - // 1. Set both guardian and relayer fields. - // 2. The transaction must be signed by both the guardian and the relayer. - // - Using Factories: +1. Create the transaction. +2. Set both guardian and relayer fields. +3. First, the sender signs the transaction. +4. Then, the guardian signs. +5. Finally, the relayer signs before broadcasting. - // 1. Create the transaction. - // 2. Set both guardian and relayer fields. - // 3. First, the sender signs the transaction. - // 4. Then, the guardian signs. - // 5. Finally, the relayer signs before broadcasting. -})().catch((e) => { - console.log({ e }); -}); +md-start -(async () => { - // ## Addresses +## Addresses - // Create an `Address` object from a bech32-encoded string: +Create an `Address` object from a bech32-encoded string: - // ``` js +``` js { // Create an Address object from a bech32-encoded string const address = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); @@ -2704,12 +2649,12 @@ import { loadAbiRegistry } from "../src/testutils"; console.log("Public key (hex-encoded):", Buffer.from(address.getPublicKey()).toString("hex")); } - // ``` +``` - // Here’s how you can create an address from a hex-encoded string using the MultiversX JavaScript SDK: - // If the HRP (human-readable part) is not provided, the SDK will use the default one ("erd"). +Here’s how you can create an address from a hex-encoded string using the MultiversX JavaScript SDK: +If the HRP (human-readable part) is not provided, the SDK will use the default one ("erd"). - // ``` js +``` js { // Create an address from a hex-encoded string with a specified HRP const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "erd"); @@ -2717,36 +2662,36 @@ import { loadAbiRegistry } from "../src/testutils"; console.log("Address (bech32-encoded):", address.toBech32()); console.log("Public key (hex-encoded):", address.toHex()); } - // ``` +``` - // #### Create an address from a raw public key +#### Create an address from a raw public key - // ``` js +``` js { const pubkey = Buffer.from("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "hex"); const addressFromPubkey = new Address(pubkey, "erd"); } - // ``` +``` - // #### Getting the shard of an address - // ``` js +#### Getting the shard of an address +``` js const addressComputer = new AddressComputer(); const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); console.log("Shard:", addressComputer.getShardOfAddress(address)); - // ``` +``` - // Checking if an address is a smart contract - // ``` js +Checking if an address is a smart contract +``` js const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm"); console.log("Is contract address:", contractAddress.isSmartContract()); - // ``` +``` - // ### Changing the default hrp - // The **LibraryConfig** class manages the default **HRP** (human-readable part) for addresses, which is set to `"erd"` by default. - // You can change the HRP when creating an address or modify it globally in **LibraryConfig**, affecting all newly created addresses. - // ``` js +### Changing the default hrp +The **LibraryConfig** class manages the default **HRP** (human-readable part) for addresses, which is set to `"erd"` by default. +You can change the HRP when creating an address or modify it globally in **LibraryConfig**, affecting all newly created addresses. +``` js console.log(LibraryConfig.DefaultAddressHrp); const defaultAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); @@ -2758,28 +2703,26 @@ import { loadAbiRegistry } from "../src/testutils"; // Reset HRP back to "erd" to avoid affecting other parts of the application. LibraryConfig.DefaultAddressHrp = "erd"; - // ``` -})().catch((e) => { - console.log({ e }); -}); +``` + +md-start -(async () => { - // ## Wallets +## Wallets - // #### Generating a mnemonic - // Mnemonic generation is based on [bip39](https://www.npmjs.com/package/bip39) and can be achieved as follows: +#### Generating a mnemonic +Mnemonic generation is based on [bip39](https://www.npmjs.com/package/bip39) and can be achieved as follows: - // ``` js +``` js const mnemonic = Mnemonic.generate(); const words = mnemonic.getWords(); console.log({ words }); - // ``` +``` - // #### Saving the mnemonic to a keystore file - // The mnemonic can be saved to a keystore file: +#### Saving the mnemonic to a keystore file +The mnemonic can be saved to a keystore file: - // ``` js +``` js { const mnemonic = Mnemonic.generate(); @@ -2790,12 +2733,12 @@ import { loadAbiRegistry } from "../src/testutils"; console.log({ filePath }); wallet.save(filePath); } - // ``` +``` - // #### Deriving secret keys from a mnemonic - // Given a mnemonic, we can derive keypairs: +#### Deriving secret keys from a mnemonic +Given a mnemonic, we can derive keypairs: - // ``` js +``` js { const mnemonic = Mnemonic.generate(); @@ -2805,12 +2748,12 @@ import { loadAbiRegistry } from "../src/testutils"; console.log("Secret key: ", secretKey.hex()); console.log("Public key: ", publicKey.hex()); } - // ``` +``` - // #### Saving a secret key to a keystore file - // The secret key can also be saved to a keystore file: +#### Saving a secret key to a keystore file +The secret key can also be saved to a keystore file: - // ``` js +``` js { const mnemonic = Mnemonic.generate(); const secretKey = mnemonic.deriveKey(); @@ -2820,12 +2763,12 @@ import { loadAbiRegistry } from "../src/testutils"; const filePath = path.join("../src", "testdata", "testwallets", "walletWithSecretKey.json"); wallet.save(filePath); } - // ``` +``` - // #### Saving a secret key to a PEM file - // We can save a secret key to a pem file. *This is not recommended as it is not secure, but it's very convenient for testing purposes.* +#### Saving a secret key to a PEM file +We can save a secret key to a pem file. *This is not recommended as it is not secure, but it's very convenient for testing purposes.* - // ``` js +``` js { const mnemonic = Mnemonic.generate(); @@ -2839,12 +2782,12 @@ import { loadAbiRegistry } from "../src/testutils"; const filePath = path.join("../src", "testdata", "testwallets", "wallet.pem"); pem.save(filePath); } - // ``` +``` - // #### Generating a KeyPair - // A `KeyPair` is a wrapper over a secret key and a public key. We can create a keypair and use it for signing or verifying. +#### Generating a KeyPair +A `KeyPair` is a wrapper over a secret key and a public key. We can create a keypair and use it for signing or verifying. - // ``` js +``` js { const keypair = KeyPair.generate(); @@ -2852,12 +2795,12 @@ import { loadAbiRegistry } from "../src/testutils"; const secretKey = keypair.getSecretKey(); const publicKey = keypair.getPublicKey(); } - // ``` +``` - // #### Loading a wallet from keystore mnemonic file - // Load a keystore that holds an encrypted mnemonic (and perform wallet derivation at the same time): +#### Loading a wallet from keystore mnemonic file +Load a keystore that holds an encrypted mnemonic (and perform wallet derivation at the same time): - // ``` js +``` js { const filePath = path.join("../src", "testdata", "testwallets", "walletWithMnemonic.json"); @@ -2875,11 +2818,11 @@ import { loadAbiRegistry } from "../src/testutils"; console.log("Secret key: ", secretKey.hex()); console.log("Address: ", address.toBech32()); } - // ``` +``` - // #### Loading a wallet from a keystore secret key file +#### Loading a wallet from a keystore secret key file - // ``` js +``` js { const filePath = path.join("../src", "testdata", "testwallets", "walletWithSecretKey.json"); @@ -2889,11 +2832,11 @@ import { loadAbiRegistry } from "../src/testutils"; console.log("Secret key: ", secretKey.hex()); console.log("Address: ", address.toBech32()); } - // ``` +``` - // #### Loading a wallet from a PEM file +#### Loading a wallet from a PEM file - // ``` js +``` js { const filePath = path.join("../src", "testdata", "testwallets", "wallet.pem"); @@ -2902,20 +2845,18 @@ import { loadAbiRegistry } from "../src/testutils"; console.log("Secret key: ", pem.secretKey.hex()); console.log("Public key: ", pem.publicKey.hex()); } - // ``` -})().catch((e) => { - console.log({ e }); -}); +``` -(async () => { - // ## Signing objects +md-start - // Signing is done using an account's secret key. To simplify this process, we provide wrappers like [Account](#creating-accounts), which streamline signing operations. - // First, we'll explore how to sign using an Account, followed by signing directly with a secret key. +## Signing objects - // #### Signing a Transaction using an Account - // We are going to assume we have an account at this point. If you don't, feel free to check out the [creating an account](#creating-accounts) section. - // ```js +Signing is done using an account's secret key. To simplify this process, we provide wrappers like [Account](#creating-accounts), which streamline signing operations. +First, we'll explore how to sign using an Account, followed by signing directly with a secret key. + +#### Signing a Transaction using an Account +We are going to assume we have an account at this point. If you don't, feel free to check out the [creating an account](#creating-accounts) section. +```js { const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); const alice = await Account.newFromPem(filePath); @@ -2931,10 +2872,10 @@ import { loadAbiRegistry } from "../src/testutils"; transaction.signature = await alice.signTransaction(transaction); console.log(transaction.toPlainObject()); } - // ``` +``` - // #### Signing a Transaction using a SecretKey - // ```js +#### Signing a Transaction using a SecretKey +```js { const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; const secretKey = UserSecretKey.fromString(secretKeyHex); @@ -2958,10 +2899,10 @@ import { loadAbiRegistry } from "../src/testutils"; console.log(transaction.toPlainObject()); } - // ``` +``` - // #### Signing a Transaction by hash - // ```js +#### Signing a Transaction by hash +```js { const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); const alice = await Account.newFromPem(filePath); @@ -2988,10 +2929,10 @@ import { loadAbiRegistry } from "../src/testutils"; console.log(transaction.toPlainObject()); } - // ``` +``` - // #### Signing a Message using an Account: - // ```js +#### Signing a Message using an Account: +```js { const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); const alice = await Account.newFromPem(filePath); @@ -3003,10 +2944,10 @@ import { loadAbiRegistry } from "../src/testutils"; message.signature = await alice.signMessage(message); } - // ``` +``` - // #### Signing a Message using an SecretKey: - // ```js +#### Signing a Message using an SecretKey: +```js { const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; const secretKey = UserSecretKey.fromString(secretKeyHex); @@ -3022,28 +2963,17 @@ import { loadAbiRegistry } from "../src/testutils"; message.signature = await secretKey.sign(serialized); } - // ``` -})().catch((e) => { - console.log({ e }); -}); - -import { - Account, - Address, - Message, - MessageComputer, - Transaction, - TransactionComputer, - UserPublicKey, - UserVerifier, -(async () => { - // ## Verifying signatures - - // Signature verification is performed using an account’s public key. - // To simplify this process, we provide wrappers over public keys that make verification easier and more convenient. - - // #### Verifying Transaction signature using a UserVerifier - // ```js +``` + +md-start + +## Verifying signatures + +Signature verification is performed using an account’s public key. +To simplify this process, we provide wrappers over public keys that make verification easier and more convenient. + +#### Verifying Transaction signature using a UserVerifier +```js { const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); const account = await Account.newFromPem(filePath); @@ -3073,11 +3003,11 @@ import { console.log("Transaction is signed by Alice: ", isSignedByAlice); } - // ``` +``` - // #### Verifying Message signature using a UserVerifier +#### Verifying Message signature using a UserVerifier - // ```ts +```ts { const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); const account = await Account.newFromPem(filePath); @@ -3103,10 +3033,10 @@ import { console.log("Message is signed by Alice: ", isSignedByAlice); } - // ``` +``` - // #### Verifying a signature using a public key - // ```js +#### Verifying a signature using a public key +```js { const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); const account = await Account.newFromPem(filePath); @@ -3135,13 +3065,13 @@ import { const isSignedByAlice = await publicKey.verify(serializedTransaction, transaction.signature); console.log("Transaction is signed by Alice: ", isSignedByAlice); } - // ``` +``` - // #### Sending messages over boundaries - // Signed Message objects are typically sent to a remote party (e.g., a service), which can then verify the signature. - // To prepare a message for transmission, you can use the `MessageComputer.packMessage()` utility method. +#### Sending messages over boundaries +Signed Message objects are typically sent to a remote party (e.g., a service), which can then verify the signature. +To prepare a message for transmission, you can use the `MessageComputer.packMessage()` utility method. - // ```js +```js { const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); const account = await Account.newFromPem(filePath); @@ -3159,11 +3089,11 @@ import { console.log("Packed message", packedMessage); } - // ``` +``` - // Then, on the receiving side, you can use [`MessageComputer.unpackMessage()`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/MessageComputer.html#unpackMessage) to reconstruct the message, prior verification: +Then, on the receiving side, you can use [`MessageComputer.unpackMessage()`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/MessageComputer.html#unpackMessage) to reconstruct the message, prior verification: - // ```js +```js { const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); const alice = await Account.newFromPem(filePath); @@ -3186,7 +3116,4 @@ import { console.log("Transaction is signed by Alice: ", isSignedByAlice); } - // ``` -})().catch((e) => { - console.log({ e }); -}); +``` diff --git a/cookbook/delegation.ts b/cookbook/delegation.ts index f667fe09..704e8430 100644 --- a/cookbook/delegation.ts +++ b/cookbook/delegation.ts @@ -1,5 +1,6 @@ import path from "path"; // md-ignore import { Account, Address, DevnetEntrypoint, TokenManagementTransactionsOutcomeParser } from "../src"; // md-ignore +// md-start (async () => { // ### Delegation management diff --git a/cookbook/entrypoints.ts b/cookbook/entrypoints.ts index 163ad031..d486df25 100644 --- a/cookbook/entrypoints.ts +++ b/cookbook/entrypoints.ts @@ -1,4 +1,5 @@ import { DevnetEntrypoint } from "../src"; // md-ignore +// md-start (async () => { // ## Overview diff --git a/cookbook/generate.py b/cookbook/generate.py index 88170254..3188c555 100644 --- a/cookbook/generate.py +++ b/cookbook/generate.py @@ -21,32 +21,21 @@ current_dir / "verifySignatures.ts" ] -MARKER_INSERT = "md-insert:" DIRECTIVE_PREFIX = "// md-" +DIRECTIVE_START = "// md-start" DIRECTIVE_IGNORE = "// md-ignore" DIRECTIVE_UNINDENT = "// md-unindent" DIRECTIVE_AS_COMMENT = "// md-as-comment" -DIRECTIVE_INSERT = f"// {MARKER_INSERT}" +TO_REMOVE = [ + """(async () => {""", + """})().catch((e) => { + console.log({ e }); +});"""] API_URL = "https://multiversx.github.io/mx-sdk-js-core" API_DEFAIULT_VERSION = "v13" DOCS_URL = "https://docs.multiversx.com" -notes: Dict[str, str] = { - "mixedTypedValuesAndNativeValues": """:::tip -When creating transactions using `class:SmartContractController` or `class:SmartContractTransactionsFactory`, even if the ABI is available and provided, -you can still use `class:TypedValue` objects as arguments for deployments and interactions. - -Even further, you can use a mix of `class:TypedValue` objects and plain JavaScript values and objects. For example: - -```js -let args = [new U32Value(42), "hello", { foo: "bar" }, new TokenIdentifierValue("TEST-abcdef")]; -``` - -:::""", -} - - def main(): output_file = current_dir / "cookbook.md" output_sections: List[str] = [] @@ -64,15 +53,18 @@ def main(): def render_file(input_file: Path) -> List[str]: input_text = input_file.read_text() + for item in TO_REMOVE: + input_text = input_text.replace(item, "") input_lines = input_text.splitlines() + start = input_lines.index(DIRECTIVE_START) + input_lines = input_lines[start:] output_lines: List[str] = [] for line in input_lines: should_ignore = DIRECTIVE_IGNORE in line should_unindent = DIRECTIVE_UNINDENT in line - is_comment = line.startswith("//") + is_comment = line.startswith("//") or line.startswith(" //") should_keep_as_comment = DIRECTIVE_AS_COMMENT in line - should_insert = DIRECTIVE_INSERT in line if should_ignore: continue @@ -81,16 +73,11 @@ def render_file(input_file: Path) -> List[str]: line = line.lstrip() if is_comment and not should_keep_as_comment: - line = line[2:].strip() + line = line.strip().strip("/").strip() line = line.replace(DIRECTIVE_UNINDENT, "") line = line.replace(DIRECTIVE_AS_COMMENT, "") - if should_insert: - box_name = line.replace(MARKER_INSERT, "").strip() - box_content = notes[box_name] - line = box_content - line = line.rstrip() output_lines.append(line) diff --git a/cookbook/guarded.ts b/cookbook/guarded.ts index 687c34b0..d3c67979 100644 --- a/cookbook/guarded.ts +++ b/cookbook/guarded.ts @@ -1,5 +1,6 @@ import path from "path"; // md-ignore import { Account, DevnetEntrypoint } from "../src"; // md-ignore +// md-start (async () => { // ### Guarded transactions // Similar to relayers, transactions also have two additional fields: diff --git a/cookbook/networkProviders.ts b/cookbook/networkProviders.ts index 309a354f..33cb9954 100644 --- a/cookbook/networkProviders.ts +++ b/cookbook/networkProviders.ts @@ -7,6 +7,7 @@ import { Token, Transaction, } from "../src"; // md-ignore +// md-start (async () => { // ## Calling the Faucet diff --git a/cookbook/relayed.ts b/cookbook/relayed.ts index b85d44af..d6166e78 100644 --- a/cookbook/relayed.ts +++ b/cookbook/relayed.ts @@ -1,5 +1,6 @@ import path from "path"; // md-ignore import { Account, Address, DevnetEntrypoint, Transaction } from "../src"; // md-ignore +// md-start (async () => { // ### Relayed transactions // We are currently on the `third iteration (V3)` of relayed transactions. V1 and V2 will soon be deactivated, so we will focus on V3. diff --git a/cookbook/signingObjects.ts b/cookbook/signingObjects.ts index 01974283..0c3979b9 100644 --- a/cookbook/signingObjects.ts +++ b/cookbook/signingObjects.ts @@ -1,5 +1,6 @@ import path from "path"; // md-ignore import { Account, Address, Message, MessageComputer, Transaction, TransactionComputer, UserSecretKey } from "../src"; // md-ignore +// md-start (async () => { // ## Signing objects diff --git a/cookbook/smartContracts.ts b/cookbook/smartContracts.ts index fa6bc2ed..5df17f91 100644 --- a/cookbook/smartContracts.ts +++ b/cookbook/smartContracts.ts @@ -21,6 +21,7 @@ import { U64Value, } from "../src"; // md-ignore import { loadAbiRegistry } from "../src/testutils"; +// md-start (async () => { // ### Smart Contracts diff --git a/cookbook/tokens.ts b/cookbook/tokens.ts index 557e310b..4b47569e 100644 --- a/cookbook/tokens.ts +++ b/cookbook/tokens.ts @@ -1,5 +1,6 @@ import path from "path"; // md-ignore import { Account, Address, DevnetEntrypoint, TokenManagementTransactionsOutcomeParser } from "../src"; // md-ignore +// md-start (async () => { // ### Token management diff --git a/cookbook/transactions.ts b/cookbook/transactions.ts index c06f1162..169ba5a6 100644 --- a/cookbook/transactions.ts +++ b/cookbook/transactions.ts @@ -9,6 +9,7 @@ import { TransfersController, TransferTransactionsFactory, } from "../src"; // md-ignore +// md-start (async () => { // ## Creating transactions diff --git a/cookbook/verifySignatures.ts b/cookbook/verifySignatures.ts index d9ef3d48..3b1071c1 100644 --- a/cookbook/verifySignatures.ts +++ b/cookbook/verifySignatures.ts @@ -9,6 +9,7 @@ import { UserPublicKey, UserVerifier, } from "../src"; // md-ignore +// md-start (async () => { // ## Verifying signatures diff --git a/cookbook/wallets.ts b/cookbook/wallets.ts index d99c88b5..4d402b2f 100644 --- a/cookbook/wallets.ts +++ b/cookbook/wallets.ts @@ -1,5 +1,6 @@ import path from "path"; // md-ignore import { KeyPair, Mnemonic, UserPem, UserWallet } from "../src"; // md-ignore +// md-start (async () => { // ## Wallets From 8f2ab9e00628d93c662f3a3ace95474c9fc99bae Mon Sep 17 00:00:00 2001 From: danielailie Date: Thu, 17 Apr 2025 15:10:37 +0300 Subject: [PATCH 09/15] Update cookbook --- cookbook/cookbook.md | 3638 +++++++++++++++++++------------------- cookbook/wallets.ts | 2 - src/wallet/userWallet.ts | 2 - 3 files changed, 1804 insertions(+), 1838 deletions(-) diff --git a/cookbook/cookbook.md b/cookbook/cookbook.md index ace1d92f..ada24e4c 100644 --- a/cookbook/cookbook.md +++ b/cookbook/cookbook.md @@ -1,5 +1,3 @@ -md-start - ## Overview This guide walks you through handling common tasks using the MultiversX Javascript SDK (v14, latest stable version). @@ -12,14 +10,14 @@ There is a dedicated entrypoint for each network: `MainnetEntrypoint`, `DevnetE For example, to create a Devnet entrypoint you have the following command: ```js - const entrypoint = new DevnetEntrypoint(); +const entrypoint = new DevnetEntrypoint(); ``` #### Using a Custom API If you'd like to connect to a third-party API, you can specify the url parameter: ```js - const apiEntrypoint = new DevnetEntrypoint("https://custom-multiversx-devnet-api.com"); +const apiEntrypoint = new DevnetEntrypoint("https://custom-multiversx-devnet-api.com"); ``` #### Using a Proxy @@ -27,63 +25,61 @@ If you'd like to connect to a third-party API, you can specify the url parameter By default, the DevnetEntrypoint uses the standard API. However, you can create a custom entrypoint that interacts with a proxy by specifying the kind parameter: ```js - const customEntrypoint = new DevnetEntrypoint("https://devnet-gateway.multiversx.com", "proxy"); +const customEntrypoint = new DevnetEntrypoint("https://devnet-gateway.multiversx.com", "proxy"); ``` -md-start - ## Creating Accounts You can initialize an account directly from the entrypoint. Keep in mind that the account is network agnostic, meaning it doesn't matter which entrypoint is used. Accounts are used for signing transactions and messages and managing the account's nonce. They can also be saved to a PEM or keystore file for future use. ```js - { - const entrypoint = new DevnetEntrypoint(); - const account = entrypoint.createAccount(); - } +{ + const entrypoint = new DevnetEntrypoint(); + const account = entrypoint.createAccount(); +} ``` ### Other Ways to Instantiate an Account #### From a Secret Key ```js - { - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); - const accountFromSecretKey = new Account(secretKey); - } + const accountFromSecretKey = new Account(secretKey); +} ``` #### From a PEM file ```js - { - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const accountFromPem = Account.newFromPem(filePath); - } +{ + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const accountFromPem = Account.newFromPem(filePath); +} ``` #### From a Keystore File ```js - { - const keystorePath = path.join("../src", "testdata", "testwallets", "alice.json"); - const accountFromKeystore = Account.newFromKeystore(keystorePath, "password"); - } +{ + const keystorePath = path.join("../src", "testdata", "testwallets", "alice.json"); + const accountFromKeystore = Account.newFromKeystore(keystorePath, "password"); +} ``` #### From a Mnemonic ```js - const mnemonic = Mnemonic.generate(); - const accountFromMnemonic = Account.newFromMnemonic(mnemonic.toString()); +const mnemonic = Mnemonic.generate(); +const accountFromMnemonic = Account.newFromMnemonic(mnemonic.toString()); ``` #### From a KeyPair ```js - const keypair = KeyPair.generate(); - const accountFromKeyPairs = Account.newFromKeypair(keypair); +const keypair = KeyPair.generate(); +const accountFromKeyPairs = Account.newFromKeypair(keypair); ``` ### Managing the Account Nonce @@ -93,21 +89,21 @@ You can fetch the nonce from the network and increment it after each transaction Each transaction must have the correct nonce, otherwise it will fail to execute. ```js - { - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const key = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const key = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); - const accountWithNonce = new Account(key); - const entrypoint = new DevnetEntrypoint(); + const accountWithNonce = new Account(key); + const entrypoint = new DevnetEntrypoint(); - // Fetch the current nonce from the network - accountWithNonce.nonce = await entrypoint.recallAccountNonce(accountWithNonce.address); + // Fetch the current nonce from the network + accountWithNonce.nonce = await entrypoint.recallAccountNonce(accountWithNonce.address); - // Create and send a transaction here... + // Create and send a transaction here... - // Increment nonce after each transaction - const nonce = accountWithNonce.getNonceThenIncrement(); - } + // Increment nonce after each transaction + const nonce = accountWithNonce.getNonceThenIncrement(); +} ``` For more details, see the [Creating Transactions](#creating-transactions) section. @@ -120,24 +116,24 @@ Keystore files offer a higher level of security. #### Saving the Account to a PEM File ```js - { - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); - const account = new Account(secretKey); - account.saveToPem(path.resolve("wallet.pem")); - } + const account = new Account(secretKey); + account.saveToPem(path.resolve("wallet.pem")); +} ``` #### Saving the Account to a Keystore File ```js - { - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = new UserSecretKey(Buffer.from(secretKeyHex, "hex")); - const account = new Account(secretKey); - account.saveToKeystore(path.resolve("keystoreWallet.json"), "password"); - } + const account = new Account(secretKey); + account.saveToKeystore(path.resolve("keystoreWallet.json"), "password"); +} ``` @@ -159,8 +155,6 @@ When signing transactions or messages, the Ledger device will prompt you to conf The `Account` implements the `IAccount` interface, making it compatible with transaction controllers and any other component that expects this interface. -md-start - ## Calling the Faucet This functionality is not yet available through the entrypoint, but we recommend using the faucet available within the Web Wallet. For more details about the faucet [see this](/wallet/web-wallet/#testnet-and-devnet-faucet). @@ -186,32 +180,32 @@ Some other methods are exposed through a so called **network provider**. To get the underlying network provider from our entrypoint, we can do as follows: ```js - { - const entrypoint = new DevnetEntrypoint(); - const networkProvider = entrypoint.createNetworkProvider(); - } +{ + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); +} ``` ### Creating a network provider When manually instantiating a network provider, you can provide a configuration to specify the client name and set custom request options. ```js - { - // Create a configuration object - const config = { - clientName: "hello-multiversx", - requestsOptions: { - timeout: 1000, // Timeout in milliseconds - auth: { - username: "user", - password: "password", - }, +{ + // Create a configuration object + const config = { + clientName: "hello-multiversx", + requestsOptions: { + timeout: 1000, // Timeout in milliseconds + auth: { + username: "user", + password: "password", }, - }; + }, + }; - // Instantiate the network provider with the config - const api = new ApiNetworkProvider("https://devnet-api.multiversx.com", config); - } + // Instantiate the network provider with the config + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com", config); +} ``` A full list of available methods for `ApiNetworkProvider` can be found [here](https://multiversx.github.io/mx-sdk-js-core/v14/classes/ApiNetworkProvider.html). @@ -225,24 +219,24 @@ The classes returned by the API expose the most commonly used fields directly fo ### Fetching the network config ```js - { - const entrypoint = new DevnetEntrypoint(); - const networkProvider = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); - const networkConfig = networkProvider.getNetworkConfig(); - } + const networkConfig = networkProvider.getNetworkConfig(); +} ``` ### Fetching the network status ```js - { - const entrypoint = new DevnetEntrypoint(); - const networkProvider = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const networkProvider = entrypoint.createNetworkProvider(); - const metaNetworkStatus = networkProvider.getNetworkStatus(); // fetches status from metachain - const networkStatus = networkProvider.getNetworkStatus(); // fetches status from shard one - } + const metaNetworkStatus = networkProvider.getNetworkStatus(); // fetches status from metachain + const networkStatus = networkProvider.getNetworkStatus(); // fetches status from shard one +} ``` ### Fetching a Block from the Network @@ -252,76 +246,76 @@ When using the **PROXY**, keep in mind that the shard must also be specified in #### Fetching a block using the **API** ```js - { - const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); - const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; - const block = await api.getBlock(blockHash); - } +{ + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); + const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; + const block = await api.getBlock(blockHash); +} ``` Additionally, we can fetch the latest block from the network: ```js - { - const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); - const latestBlock = await api.getLatestBlock(); - } +{ + const api = new ApiNetworkProvider("https://devnet-api.multiversx.com"); + const latestBlock = await api.getLatestBlock(); +} ``` #### Fetching a block using the **PROXY** When using the proxy, we have to provide the shard, as well. ```js - { - const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); - const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; - const block = proxy.getBlock({ blockHash, shard: 1 }); - } +{ + const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); + const blockHash = "1147e111ce8dd860ae43a0f0d403da193a940bfd30b7d7f600701dd5e02f347a"; + const block = proxy.getBlock({ blockHash, shard: 1 }); +} ``` We can also fetch the latest block from the network. By default, the shard will be the metachain, but we can specify a different shard if needed. ```js - { - const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); - const latestBlock = proxy.getLatestBlock(); - } +{ + const proxy = new ProxyNetworkProvider("https://devnet-api.multiversx.com"); + const latestBlock = proxy.getLatestBlock(); +} ``` ### Fetching an Account To fetch an account, we need its address. Once we have the address, we create an `Address` object and pass it as an argument to the method. ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const account = await api.getAccount(alice); - } +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccount(alice); +} ``` ### Fetching an Account's Storage We can also fetch an account's storage, allowing us to retrieve all key-value pairs saved for that account. ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const account = await api.getAccountStorage(alice); - } +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccountStorage(alice); +} ``` If we only want to fetch a specific key, we can do so as follows: ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const account = await api.getAccountStorageEntry(alice, "testKey"); - } +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.getAccountStorageEntry(alice, "testKey"); +} ``` ### Waiting for an Account to Meet a Condition @@ -333,16 +327,16 @@ To implement this, we need to define the condition to check each time the accoun Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const condition = (account: any) => { - return account.balance >= 7000000000000000000n; // 7 EGLD - }; - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const account = await api.awaitAccountOnCondition(alice, condition); - } + const condition = (account: any) => { + return account.balance >= 7000000000000000000n; // 7 EGLD + }; + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const account = await api.awaitAccountOnCondition(alice, condition); +} ``` ### Sending and Simulating Transactions @@ -351,247 +345,247 @@ To execute transactions, we use the network providers to broadcast them to the n #### Sending a Transaction ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - const transaction = new Transaction({ - sender: alice, - receiver: bob, - gasLimit: 50000n, - chainID: "D", - }); + const transaction = new Transaction({ + sender: alice, + receiver: bob, + gasLimit: 50000n, + chainID: "D", + }); - // set the correct nonce and sign the transaction ... + // set the correct nonce and sign the transaction ... - const transactionHash = await api.sendTransaction(transaction); - } + const transactionHash = await api.sendTransaction(transaction); +} ``` #### Sending multiple transactions ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); - - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - - const firstTransaction = new Transaction({ - sender: alice, - receiver: bob, - gasLimit: 50000n, - chainID: "D", - nonce: 2n, - }); - - const secondTransaction = new Transaction({ - sender: bob, - receiver: alice, - gasLimit: 50000n, - chainID: "D", - nonce: 1n, - }); - - const thirdTransaction = new Transaction({ - sender: alice, - receiver: alice, - gasLimit: 60000n, - chainID: "D", - nonce: 3n, - data: new Uint8Array(Buffer.from("hello")), - }); - - // set the correct nonce and sign the transaction ... - - const [numOfSentTxs, hashes] = await api.sendTransactions([ - firstTransaction, - secondTransaction, - thirdTransaction, - ]); - } +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); + + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const firstTransaction = new Transaction({ + sender: alice, + receiver: bob, + gasLimit: 50000n, + chainID: "D", + nonce: 2n, + }); + + const secondTransaction = new Transaction({ + sender: bob, + receiver: alice, + gasLimit: 50000n, + chainID: "D", + nonce: 1n, + }); + + const thirdTransaction = new Transaction({ + sender: alice, + receiver: alice, + gasLimit: 60000n, + chainID: "D", + nonce: 3n, + data: new Uint8Array(Buffer.from("hello")), + }); + + // set the correct nonce and sign the transaction ... + + const [numOfSentTxs, hashes] = await api.sendTransactions([ + firstTransaction, + secondTransaction, + thirdTransaction, + ]); +} ``` #### Simulating transactions A transaction can be simulated before being sent for processing by the network. This is primarily used for smart contract calls, allowing you to preview the results produced by the smart contract. ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); - const transaction = new Transaction({ - sender: alice, - receiver: contract, - gasLimit: 5000000n, - chainID: "D", - data: new Uint8Array(Buffer.from("add@07")), - }); + const transaction = new Transaction({ + sender: alice, + receiver: contract, + gasLimit: 5000000n, + chainID: "D", + data: new Uint8Array(Buffer.from("add@07")), + }); - const transactionOnNetwork = await api.simulateTransaction(transaction); - } + const transactionOnNetwork = await api.simulateTransaction(transaction); +} ``` #### Estimating the gas cost of a transaction Before sending a transaction to the network for processing, you can retrieve the estimated gas limit required for the transaction to be executed. ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqccmyzj9sade2495w78h42erfrw7qmqxpd8sss6gmgn"); - const nonce = await entrypoint.recallAccountNonce(alice); + const nonce = await entrypoint.recallAccountNonce(alice); - const transaction = new Transaction({ - sender: alice, - receiver: contract, - gasLimit: 5000000n, - chainID: "D", - data: new Uint8Array(Buffer.from("add@07")), - nonce: nonce, - }); + const transaction = new Transaction({ + sender: alice, + receiver: contract, + gasLimit: 5000000n, + chainID: "D", + data: new Uint8Array(Buffer.from("add@07")), + nonce: nonce, + }); - const transactionCostResponse = await api.estimateTransactionCost(transaction); - } + const transactionCostResponse = await api.estimateTransactionCost(transaction); +} ``` ### Waiting for transaction completion After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const txHash = "exampletransactionhash"; - const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); - } + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); +} ``` ### Waiting for a Transaction to Satisfy a Condition Similar to accounts, we can wait until a transaction meets a specific condition. ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const condition = (txOnNetwork: any) => !txOnNetwork.status.isSuccessful(); + const condition = (txOnNetwork: any) => !txOnNetwork.status.isSuccessful(); - const txHash = "exampletransactionhash"; - const transactionOnNetwork = await api.awaitTransactionOnCondition(txHash, condition); - } + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionOnCondition(txHash, condition); +} ``` ### Waiting for transaction completion After sending a transaction, you may want to wait until it is processed before proceeding with another action. Keep in mind that this method has a default timeout, which can be adjusted using the `AwaitingOptions` class. ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const txHash = "exampletransactionhash"; - const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); - } + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.awaitTransactionCompleted(txHash); +} ``` ### Fetching Transactions from the Network After sending a transaction, we can fetch it from the network using the transaction hash, which we receive after broadcasting the transaction. ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const txHash = "exampletransactionhash"; - const transactionOnNetwork = await api.getTransaction(txHash); - } + const txHash = "exampletransactionhash"; + const transactionOnNetwork = await api.getTransaction(txHash); +} ``` ### Fetching a token from an account We can fetch a specific token (ESDT, MetaESDT, SFT, NFT) from an account by providing the account's address and the token identifier. ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - let token = new Token({ identifier: "TEST-ff155e" }); // ESDT - let tokenOnNetwork = await api.getTokenOfAccount(alice, token); + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + let token = new Token({ identifier: "TEST-ff155e" }); // ESDT + let tokenOnNetwork = await api.getTokenOfAccount(alice, token); - token = new Token({ identifier: "NFT-987654", nonce: 11n }); // NFT - tokenOnNetwork = await api.getTokenOfAccount(alice, token); - } + token = new Token({ identifier: "NFT-987654", nonce: 11n }); // NFT + tokenOnNetwork = await api.getTokenOfAccount(alice, token); +} ``` ### Fetching all fungible tokens of an account Fetches all fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const fungibleTokens = await api.getFungibleTokensOfAccount(alice); - } + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const fungibleTokens = await api.getFungibleTokensOfAccount(alice); +} ``` ### Fetching all non-fungible tokens of an account Fetches all non-fungible tokens held by an account. Note that this method does not handle pagination, but it can be achieved using `doGetGeneric`. ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const nfts = await api.getNonFungibleTokensOfAccount(alice); - } + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const nfts = await api.getNonFungibleTokensOfAccount(alice); +} ``` ### Fetching token metadata If we want to fetch the metadata of a token (e.g., owner, decimals, etc.), we can use the following methods: ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - // used for ESDT - const fungibleTokenDefinition = await api.getDefinitionOfFungibleToken("TEST-ff155e"); + // used for ESDT + const fungibleTokenDefinition = await api.getDefinitionOfFungibleToken("TEST-ff155e"); - // used for METAESDT, SFT, NFT - const nonFungibleTokenDefinition = await api.getDefinitionOfTokenCollection("NFTEST-ec88b8"); - } + // used for METAESDT, SFT, NFT + const nonFungibleTokenDefinition = await api.getDefinitionOfTokenCollection("NFTEST-ec88b8"); +} ``` ### Querying Smart Contracts Smart contract queries, or view functions, are endpoints that only read data from the contract. To send a query to the observer nodes, we can proceed as follows: ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const query = new SmartContractQuery({ - contract: Address.newFromBech32("erd1qqqqqqqqqqqqqpgqqy34h7he2ya6qcagqre7ur7cc65vt0mxrc8qnudkr4"), - function: "getSum", - arguments: [], - }); - const response = await api.queryContract(query); - } + const query = new SmartContractQuery({ + contract: Address.newFromBech32("erd1qqqqqqqqqqqqqpgqqy34h7he2ya6qcagqre7ur7cc65vt0mxrc8qnudkr4"), + function: "getSum", + arguments: [], + }); + const response = await api.queryContract(query); +} ``` ### Custom Api/Proxy calls @@ -599,19 +593,17 @@ The methods exposed by the `ApiNetworkProvider` or `ProxyNetworkProvider` are th Let’s assume we want to retrieve all the transactions sent by Alice in which the `delegate` function was called. ```js - { - const entrypoint = new DevnetEntrypoint(); - const api = entrypoint.createNetworkProvider(); +{ + const entrypoint = new DevnetEntrypoint(); + const api = entrypoint.createNetworkProvider(); - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const url = `transactions/${alice.toBech32()}?function=delegate`; + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const url = `transactions/${alice.toBech32()}?function=delegate`; - const response = await api.doGetGeneric(url); - } + const response = await api.doGetGeneric(url); +} ``` -md-start - ## Creating transactions In this section, we’ll explore how to create different types of transactions. To create transactions, we can use either controllers or factories. @@ -627,19 +619,19 @@ There are two ways to create controllers and factories: 2. Manually instantiate them. ```js - { - const entrypoint = new DevnetEntrypoint(); +{ + const entrypoint = new DevnetEntrypoint(); - // getting the controller and the factory from the entrypoint - const transfersController = entrypoint.createTransfersController(); - const transfersFactory = entrypoint.createTransfersTransactionsFactory(); + // getting the controller and the factory from the entrypoint + const transfersController = entrypoint.createTransfersController(); + const transfersFactory = entrypoint.createTransfersTransactionsFactory(); - // manually instantiating the controller and the factory - const controller = new TransfersController({ chainID: "D" }); + // manually instantiating the controller and the factory + const controller = new TransfersController({ chainID: "D" }); - const config = new TransactionsFactoryConfig({ chainID: "D" }); - const factory = new TransferTransactionsFactory({ config }); - } + const config = new TransactionsFactoryConfig({ chainID: "D" }); + const factory = new TransferTransactionsFactory({ config }); +} ``` ### Token transfers @@ -648,28 +640,28 @@ We can send both native tokens (EGLD) and ESDT tokens using either the controlle When using the controller, the transaction will be signed because we’ll be working with an Account. ```js - { - const entrypoint = new DevnetEntrypoint(); +{ + const entrypoint = new DevnetEntrypoint(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const transfersController = entrypoint.createTransfersController(); - const transaction = await transfersController.createTransactionForTransfer( - alice, - alice.getNonceThenIncrement(), - { - receiver: bob, - nativeAmount: 1n, - }, - ); + const transfersController = entrypoint.createTransfersController(); + const transaction = await transfersController.createTransactionForTransfer( + alice, + alice.getNonceThenIncrement(), + { + receiver: bob, + nativeAmount: 1n, + }, + ); - const txHash = await entrypoint.sendTransaction(transaction); - } + const txHash = await entrypoint.sendTransaction(transaction); +} ``` If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. @@ -679,31 +671,31 @@ When using the factory, only the sender's address is required. As a result, the You will need to handle these aspects after the transaction is created. ```js - { - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTransfersTransactionsFactory(); +{ + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTransfersTransactionsFactory(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - const transaction = factory.createTransactionForTransfer(alice.address, { - receiver: bob, - nativeAmount: 1000000000000000000n, - }); + const transaction = factory.createTransactionForTransfer(alice.address, { + receiver: bob, + nativeAmount: 1000000000000000000n, + }); - // set the sender's nonce - transaction.nonce = alice.getNonceThenIncrement(); + // set the sender's nonce + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction using the sender's account - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction using the sender's account + transaction.signature = await alice.signTransaction(transaction); - const txHash = await entrypoint.sendTransaction(transaction); - } + const txHash = await entrypoint.sendTransaction(transaction); +} ``` If you know you’ll only be sending native tokens, you can create the transaction using the `createTransactionForNativeTokenTransfer` method. @@ -711,37 +703,37 @@ If you know you’ll only be sending native tokens, you can create the transacti #### Custom token transfers using the controller ```js - { - const entrypoint = new DevnetEntrypoint(); +{ + const entrypoint = new DevnetEntrypoint(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const esdt = new Token({ identifier: "TEST-123456" }); - const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); + const esdt = new Token({ identifier: "TEST-123456" }); + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); - const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); - const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); - const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); - const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); + const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); + const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); - const transfersController = entrypoint.createTransfersController(); - const transaction = await transfersController.createTransactionForTransfer( - alice, - alice.getNonceThenIncrement(), - { - receiver: bob, - tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], - }, - ); + const transfersController = entrypoint.createTransfersController(); + const transaction = await transfersController.createTransactionForTransfer( + alice, + alice.getNonceThenIncrement(), + { + receiver: bob, + tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], + }, + ); - const txHash = await entrypoint.sendTransaction(transaction); - } + const txHash = await entrypoint.sendTransaction(transaction); +} ``` If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. @@ -750,39 +742,39 @@ If you know you'll only send ESDT tokens, the same transaction can be created us When using the factory, only the sender's address is required. As a result, the transaction won’t be signed, and the nonce field won’t be set correctly. These aspects should be handled after the transaction is created. ```js - { - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTransfersTransactionsFactory(); +{ + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTransfersTransactionsFactory(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const esdt = new Token({ identifier: "TEST-123456" }); // fungible tokens don't have a nonce - const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); // we set the desired amount we want to send + const esdt = new Token({ identifier: "TEST-123456" }); // fungible tokens don't have a nonce + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); // we set the desired amount we want to send - const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); - const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); // for NFTs we set the amount to `1` + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); // for NFTs we set the amount to `1` - const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); - const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); // for SFTs we set the desired amount we want to send + const sft = new Token({ identifier: "SFT-987654", nonce: 10n }); + const thirdTransfer = new TokenTransfer({ token: sft, amount: 7n }); // for SFTs we set the desired amount we want to send - const transaction = factory.createTransactionForTransfer(alice.address, { - receiver: bob, - tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], - }); + const transaction = factory.createTransactionForTransfer(alice.address, { + receiver: bob, + tokenTransfers: [firstTransfer, secondTransfer, thirdTransfer], + }); - // set the sender's nonce - transaction.nonce = alice.getNonceThenIncrement(); + // set the sender's nonce + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction using the sender's account - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction using the sender's account + transaction.signature = await alice.signTransaction(transaction); - const txHash = await entrypoint.sendTransaction(transaction); - } + const txHash = await entrypoint.sendTransaction(transaction); +} ``` If you know you'll only send ESDT tokens, the same transaction can be created using createTransactionForEsdtTokenTransfer. @@ -792,39 +784,37 @@ Both native and custom tokens can now be sent. If a `nativeAmount` is provided a We can send both types of tokens using either the `controller` or the `factory`, but for simplicity, we’ll use the controller in this example. ```js - { - const entrypoint = new DevnetEntrypoint(); +{ + const entrypoint = new DevnetEntrypoint(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const esdt = new Token({ identifier: "TEST-123456" }); - const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); + const esdt = new Token({ identifier: "TEST-123456" }); + const firstTransfer = new TokenTransfer({ token: esdt, amount: 1000000000n }); - const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); - const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); + const nft = new Token({ identifier: "NFT-987654", nonce: 10n }); + const secondTransfer = new TokenTransfer({ token: nft, amount: 1n }); - const transfersController = entrypoint.createTransfersController(); - const transaction = await transfersController.createTransactionForTransfer( - alice, - alice.getNonceThenIncrement(), - { - receiver: bob, - nativeAmount: 1000000000000000000n, - tokenTransfers: [firstTransfer, secondTransfer], - }, - ); + const transfersController = entrypoint.createTransfersController(); + const transaction = await transfersController.createTransactionForTransfer( + alice, + alice.getNonceThenIncrement(), + { + receiver: bob, + nativeAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer], + }, + ); - const txHash = await entrypoint.sendTransaction(transaction); - } + const txHash = await entrypoint.sendTransaction(transaction); +} ``` -md-start - ### Smart Contracts #### Contract ABIs @@ -834,22 +824,22 @@ While interactions with the contract are possible without the ABI, they are much #### Loading the ABI from a file ```js - { - let abiJson = await promises.readFile("../src/testData/adder.abi.json", { encoding: "utf8" }); - let abiObj = JSON.parse(abiJson); - let abi = Abi.create(abiObj); - } +{ + let abiJson = await promises.readFile("../src/testData/adder.abi.json", { encoding: "utf8" }); + let abiObj = JSON.parse(abiJson); + let abi = Abi.create(abiObj); +} ``` #### Loading the ABI from an URL ```js - { - const response = await axios.get( - "https://github.com/multiversx/mx-sdk-js-core/raw/main/src/testdata/adder.abi.json", - ); - let abi = Abi.create(response.data); - } +{ + const response = await axios.get( + "https://github.com/multiversx/mx-sdk-js-core/raw/main/src/testdata/adder.abi.json", + ); + let abi = Abi.create(response.data); +} ``` #### Manually construct the ABI @@ -857,36 +847,36 @@ While interactions with the contract are possible without the ABI, they are much If an ABI file isn’t available, but you know the contract’s endpoints and data types, you can manually construct the ABI. ```js - { - let abi = Abi.create({ - endpoints: [ - { - name: "add", - inputs: [], - outputs: [], - }, - ], - }); - } -``` - -```js - { - let abi = Abi.create({ - endpoints: [ - { - name: "foo", - inputs: [{ type: "BigUint" }, { type: "u32" }, { type: "Address" }], - outputs: [{ type: "u32" }], - }, - { - name: "bar", - inputs: [{ type: "counted-variadic" }, { type: "variadic" }], - outputs: [], - }, - ], - }); - } +{ + let abi = Abi.create({ + endpoints: [ + { + name: "add", + inputs: [], + outputs: [], + }, + ], + }); +} +``` + +```js +{ + let abi = Abi.create({ + endpoints: [ + { + name: "foo", + inputs: [{ type: "BigUint" }, { type: "u32" }, { type: "Address" }], + outputs: [{ type: "u32" }], + }, + { + name: "bar", + inputs: [{ type: "counted-variadic" }, { type: "variadic" }], + outputs: [], + }, + ], + }); +} ``` ### Smart Contract deployments @@ -897,35 +887,35 @@ This allows arguments to be passed as native Javascript values. If the ABI is no #### Deploying a Smart Contract Using the Controller ```js - { - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const sender = await Account.newFromPem(filePath); - const entrypoint = new DevnetEntrypoint(); +{ + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); - // the developer is responsible for managing the nonce - sender.nonce = await entrypoint.recallAccountNonce(sender.address); + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); - // load the contract bytecode - const bytecode = await promises.readFile("../src/testData/adder.wasm"); - // load the abi file - const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + // load the contract bytecode + const bytecode = await promises.readFile("../src/testData/adder.wasm"); + // load the abi file + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); - const controller = entrypoint.createSmartContractController(abi); + const controller = entrypoint.createSmartContractController(abi); - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: - let args: any[] = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: - args = [42]; + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; - const deployTransaction = await controller.createTransactionForDeploy(sender, sender.getNonceThenIncrement(), { - bytecode: bytecode, - gasLimit: 6000000n, - arguments: args, - }); + const deployTransaction = await controller.createTransactionForDeploy(sender, sender.getNonceThenIncrement(), { + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }); - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(deployTransaction); - } + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(deployTransaction); +} ``` :::tip @@ -942,32 +932,32 @@ let args = [new U32Value(42), "hello", { foo: "bar" }, new TokenIdentifierValue( #### Parsing contract deployment transactions ```js - { - // We use the transaction hash we got when broadcasting the transaction +{ + // We use the transaction hash we got when broadcasting the transaction - const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createSmartContractController(abi); - const outcome = await controller.awaitCompletedDeploy("txHash"); // waits for transaction completion and parses the result - const contractAddress = outcome.contracts[0].address; - } + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createSmartContractController(abi); + const outcome = await controller.awaitCompletedDeploy("txHash"); // waits for transaction completion and parses the result + const contractAddress = outcome.contracts[0].address; +} ``` If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: ```js - { - // We use the transaction hash we got when broadcasting the transaction - // If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: +{ + // We use the transaction hash we got when broadcasting the transaction + // If we want to wait for transaction completion and parse the result in two different steps, we can do as follows: - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createSmartContractController(); - const networkProvider = entrypoint.createNetworkProvider(); - const transactionOnNetwork = await networkProvider.awaitTransactionCompleted("txHash"); + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createSmartContractController(); + const networkProvider = entrypoint.createNetworkProvider(); + const transactionOnNetwork = await networkProvider.awaitTransactionCompleted("txHash"); - // parsing the transaction - const outcome = await controller.parseDeploy(transactionOnNetwork); - } + // parsing the transaction + const outcome = await controller.parseDeploy(transactionOnNetwork); +} ``` #### Computing the contract address @@ -975,80 +965,80 @@ If we want to wait for transaction completion and parse the result in two differ Even before broadcasting, at the moment you know the sender's address and the nonce for your deployment transaction, you can (deterministically) compute the (upcoming) address of the smart contract: ```js - { - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createSmartContractTransactionsFactory(); - const bytecode = await promises.readFile("../contracts/adder.wasm"); - - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: - let args: any[] = [new BigUIntValue(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: - args = [42]; - - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const deployTransaction = factory.createTransactionForDeploy(alice.address, { - bytecode: bytecode, - gasLimit: 6000000n, - arguments: args, - }); - const addressComputer = new AddressComputer(); - const contractAddress = addressComputer.computeContractAddress( - deployTransaction.sender, - deployTransaction.nonce, - ); +{ + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createSmartContractTransactionsFactory(); + const bytecode = await promises.readFile("../contracts/adder.wasm"); + + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new BigUIntValue(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const deployTransaction = factory.createTransactionForDeploy(alice.address, { + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }); + const addressComputer = new AddressComputer(); + const contractAddress = addressComputer.computeContractAddress( + deployTransaction.sender, + deployTransaction.nonce, + ); - console.log("Contract address:", contractAddress.toBech32()); - } + console.log("Contract address:", contractAddress.toBech32()); +} ``` #### Deploying a Smart Contract using the factory After the transaction is created the nonce needs to be properly set and the transaction should be signed before broadcasting it. ```js - { - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createSmartContractTransactionsFactory(); +{ + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createSmartContractTransactionsFactory(); - // load the contract bytecode - const bytecode = await promises.readFile("../src/testData/adder.wasm"); + // load the contract bytecode + const bytecode = await promises.readFile("../src/testData/adder.wasm"); - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: - let args: any[] = [new BigUIntValue(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: - args = [42]; + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new BigUIntValue(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const deployTransaction = await factory.createTransactionForDeploy(alice.address, { - bytecode: bytecode, - gasLimit: 6000000n, - arguments: args, - }); + const deployTransaction = await factory.createTransactionForDeploy(alice.address, { + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }); - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // set the nonce - deployTransaction.nonce = alice.nonce; + // set the nonce + deployTransaction.nonce = alice.nonce; - // sign the transaction - deployTransaction.signature = await alice.signTransaction(deployTransaction); + // sign the transaction + deployTransaction.signature = await alice.signTransaction(deployTransaction); - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(deployTransaction); + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(deployTransaction); - // waiting for transaction to complete - const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + // waiting for transaction to complete + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - // parsing transaction - const parser = new SmartContractTransactionsOutcomeParser(); - const parsedOutcome = parser.parseDeploy({ transactionOnNetwork }); - const contractAddress = parsedOutcome.contracts[0].address; + // parsing transaction + const parser = new SmartContractTransactionsOutcomeParser(); + const parsedOutcome = parser.parseDeploy({ transactionOnNetwork }); + const contractAddress = parsedOutcome.contracts[0].address; - console.log(contractAddress.toBech32()); - } + console.log(contractAddress.toBech32()); +} ``` ### Smart Contract calls @@ -1058,51 +1048,51 @@ In this section we'll see how we can call an endpoint of our previously deployed #### Calling a smart contract using the controller ```js - { - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const sender = await Account.newFromPem(filePath); - const entrypoint = new DevnetEntrypoint(); +{ + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); - // the developer is responsible for managing the nonce - sender.nonce = await entrypoint.recallAccountNonce(sender.address); + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); - // load the abi file - const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); - const controller = entrypoint.createSmartContractController(abi); + // load the abi file + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + const controller = entrypoint.createSmartContractController(abi); - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: - let args: any[] = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: - args = [42]; + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; - const transaction = await controller.createTransactionForExecute(sender, sender.getNonceThenIncrement(), { - contract: contractAddress, - gasLimit: 5000000n, - function: "add", - arguments: args, - }); + const transaction = await controller.createTransactionForExecute(sender, sender.getNonceThenIncrement(), { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + }); - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(transaction); + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); - console.log(txHash); - } + console.log(txHash); +} ``` #### Parsing smart contract call transactions In our case, calling the add endpoint does not return anything, but similar to the example above, we could parse this transaction to get the output values of a smart contract call. ```js - { - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createSmartContractController(); - const txHash = "b3ae88ad05c464a74db73f4013de05abcfcb4fb6647c67a262a6cfdf330ef4a9"; - // waits for transaction completion and parses the result - const parsedOutcome = await controller.awaitCompletedExecute(txHash); - const values = parsedOutcome.values; - } +{ + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createSmartContractController(); + const txHash = "b3ae88ad05c464a74db73f4013de05abcfcb4fb6647c67a262a6cfdf330ef4a9"; + // waits for transaction completion and parses the result + const parsedOutcome = await controller.awaitCompletedExecute(txHash); + const values = parsedOutcome.values; +} ``` #### Calling a smart contract and sending tokens (transfer & execute) @@ -1110,111 +1100,111 @@ Additionally, if an endpoint requires a payment when called, we can send tokens Both EGLD and ESDT tokens or a combination of both can be sent. This functionality is supported by both the controller and the factory. ```js - { - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const sender = await Account.newFromPem(filePath); - const entrypoint = new DevnetEntrypoint(); +{ + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const sender = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); - // the developer is responsible for managing the nonce - sender.nonce = await entrypoint.recallAccountNonce(sender.address); + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); - // load the abi file - const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + // load the abi file + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); - // get the smart contracts controller - const controller = entrypoint.createSmartContractController(abi); + // get the smart contracts controller + const controller = entrypoint.createSmartContractController(abi); - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: - let args: any[] = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: - args = [42]; + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; - // creating the transfers - const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10n }); - const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); + // creating the transfers + const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10n }); + const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); - const secondToken = new Token({ identifier: "BAR-c80d29" }); - const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); + const secondToken = new Token({ identifier: "BAR-c80d29" }); + const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); - const transaction = await controller.createTransactionForExecute(sender, sender.getNonceThenIncrement(), { - contract: contractAddress, - gasLimit: 5000000n, - function: "add", - arguments: args, - nativeTransferAmount: 1000000000000000000n, - tokenTransfers: [firstTransfer, secondTransfer], - }); + const transaction = await controller.createTransactionForExecute(sender, sender.getNonceThenIncrement(), { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + nativeTransferAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer], + }); - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(transaction); + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); - console.log(txHash); - } + console.log(txHash); +} ``` #### Calling a smart contract using the factory Let's create the same smart contract call transaction, but using the `factory`. ```js - { - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const entrypoint = new DevnetEntrypoint(); +{ + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const entrypoint = new DevnetEntrypoint(); - // the developer is responsible for managing the nonce - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // the developer is responsible for managing the nonce + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // get the smart contracts controller - const controller = entrypoint.createSmartContractTransactionsFactory(); + // get the smart contracts controller + const controller = entrypoint.createSmartContractTransactionsFactory(); - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: - let args: any[] = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: - args = [42]; + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; - // creating the transfers - const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10n }); - const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); + // creating the transfers + const firstToken = new Token({ identifier: "TEST-38f249", nonce: 10n }); + const firstTransfer = new TokenTransfer({ token: firstToken, amount: 1n }); - const secondToken = new Token({ identifier: "BAR-c80d29" }); - const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); + const secondToken = new Token({ identifier: "BAR-c80d29" }); + const secondTransfer = new TokenTransfer({ token: secondToken, amount: 10000000000000000000n }); - const transaction = await controller.createTransactionForExecute(alice.address, { - contract: contractAddress, - gasLimit: 5000000n, - function: "add", - arguments: args, - nativeTransferAmount: 1000000000000000000n, - tokenTransfers: [firstTransfer, secondTransfer], - }); + const transaction = await controller.createTransactionForExecute(alice.address, { + contract: contractAddress, + gasLimit: 5000000n, + function: "add", + arguments: args, + nativeTransferAmount: 1000000000000000000n, + tokenTransfers: [firstTransfer, secondTransfer], + }); - transaction.nonce = alice.getNonceThenIncrement(); - transaction.signature = await alice.signTransaction(transaction); + transaction.nonce = alice.getNonceThenIncrement(); + transaction.signature = await alice.signTransaction(transaction); - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(transaction); + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(transaction); - console.log(txHash); - } + console.log(txHash); +} ``` #### Parsing transaction outcome As said before, the `add` endpoint we called does not return anything, but we could parse the outcome of smart contract call transactions, as follows: ```js - { - // load the abi file - const entrypoint = new DevnetEntrypoint(); - const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); - const parser = new SmartContractTransactionsOutcomeParser({ abi }); - const txHash = "b3ae88ad05c464a74db73f4013de05abcfcb4fb6647c67a262a6cfdf330ef4a9"; - const transactionOnNetwork = await entrypoint.getTransaction(txHash); - const outcome = parser.parseExecute({ transactionOnNetwork }); - } +{ + // load the abi file + const entrypoint = new DevnetEntrypoint(); + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + const parser = new SmartContractTransactionsOutcomeParser({ abi }); + const txHash = "b3ae88ad05c464a74db73f4013de05abcfcb4fb6647c67a262a6cfdf330ef4a9"; + const transactionOnNetwork = await entrypoint.getTransaction(txHash); + const outcome = parser.parseExecute({ transactionOnNetwork }); +} ``` #### Decoding transaction events @@ -1225,16 +1215,16 @@ Suppose we'd like to decode a `startPerformAction` event emitted by the [multisi First, we load the abi file, then we fetch the transaction, we extract the event from the transaction and then we parse it. ```js - { - // load the abi files - const entrypoint = new DevnetEntrypoint(); - const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); - const parser = new TransactionEventsParser({ abi }); - const txHash = "b3ae88ad05c464a74db73f4013de05abcfcb4fb6647c67a262a6cfdf330ef4a9"; - const transactionOnNetwork = await entrypoint.getTransaction(txHash); - const events = gatherAllEvents(transactionOnNetwork); - const outcome = parser.parseEvents({ events }); - } +{ + // load the abi files + const entrypoint = new DevnetEntrypoint(); + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + const parser = new TransactionEventsParser({ abi }); + const txHash = "b3ae88ad05c464a74db73f4013de05abcfcb4fb6647c67a262a6cfdf330ef4a9"; + const transactionOnNetwork = await entrypoint.getTransaction(txHash); + const events = gatherAllEvents(transactionOnNetwork); + const outcome = parser.parseEvents({ events }); +} ``` #### Decoding transaction events @@ -1242,38 +1232,38 @@ Whenever needed, the contract ABI can be used for manually encoding or decoding Let's encode a struct called EsdtTokenPayment (of [multisig](https://github.com/multiversx/mx-contracts-rs/tree/main/contracts/multisig) contract) into binary data. ```js - { - const abi = await loadAbiRegistry("../src/testdata/multisig-full.abi.json"); - const paymentType = abi.getStruct("EsdtTokenPayment"); - const codec = new BinaryCodec(); +{ + const abi = await loadAbiRegistry("../src/testdata/multisig-full.abi.json"); + const paymentType = abi.getStruct("EsdtTokenPayment"); + const codec = new BinaryCodec(); - const paymentStruct = new Struct(paymentType, [ - new Field(new TokenIdentifierValue("TEST-8b028f"), "token_identifier"), - new Field(new U64Value(0n), "token_nonce"), - new Field(new BigUIntValue(10000n), "amount"), - ]); + const paymentStruct = new Struct(paymentType, [ + new Field(new TokenIdentifierValue("TEST-8b028f"), "token_identifier"), + new Field(new U64Value(0n), "token_nonce"), + new Field(new BigUIntValue(10000n), "amount"), + ]); - const encoded = codec.encodeNested(paymentStruct); + const encoded = codec.encodeNested(paymentStruct); - console.log(encoded.toString("hex")); - } + console.log(encoded.toString("hex")); +} ``` Now let's decode a struct using the ABI. ```js - { - const abi = await loadAbiRegistry("../src/testdata/multisig-full.abi.json"); - const actionStructType = abi.getEnum("Action"); - const data = Buffer.from( - "0500000000000000000500d006f73c4221216fa679bc559005584c4f1160e569e1000000012a0000000003616464000000010000000107", - "hex", - ); +{ + const abi = await loadAbiRegistry("../src/testdata/multisig-full.abi.json"); + const actionStructType = abi.getEnum("Action"); + const data = Buffer.from( + "0500000000000000000500d006f73c4221216fa679bc559005584c4f1160e569e1000000012a0000000003616464000000010000000107", + "hex", + ); - const codec = new BinaryCodec(); - const [decoded] = codec.decodeNested(data, actionStructType); - const decodedValue = decoded.valueOf(); - console.log(JSON.stringify(decodedValue, null, 4)); - } + const codec = new BinaryCodec(); + const [decoded] = codec.decodeNested(data, actionStructType); + const decodedValue = decoded.valueOf(); + console.log(JSON.stringify(decodedValue, null, 4)); +} ``` ### Smart Contract queries @@ -1282,43 +1272,43 @@ To perform this query, we use the **SmartContractController**. While we can use In this example, we will query the **adder smart contract** by calling its `getSum` endpoint. ```js - { - const entrypoint = new DevnetEntrypoint(); - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); +{ + const entrypoint = new DevnetEntrypoint(); + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); - // create the controller - const controller = entrypoint.createSmartContractController(abi); + // create the controller + const controller = entrypoint.createSmartContractController(abi); - // creates the query, runs the query, parses the result - const response = await controller.query({ contract: contractAddress, function: "getSum", arguments: [] }); - } + // creates the query, runs the query, parses the result + const response = await controller.query({ contract: contractAddress, function: "getSum", arguments: [] }); +} ``` If we need more granular control, we can split the process into three steps: **create the query, run the query, and parse the query response**. This approach achieves the same result as the previous example. ```js - { - const entrypoint = new DevnetEntrypoint(); +{ + const entrypoint = new DevnetEntrypoint(); - // load the abi - const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + // load the abi + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); - // the contract address we'll query - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + // the contract address we'll query + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - // create the controller - const controller = entrypoint.createSmartContractController(abi); + // create the controller + const controller = entrypoint.createSmartContractController(abi); - // create the query - const query = await controller.createQuery({ contract: contractAddress, function: "getSum", arguments: [] }); - // runs the query - const response = await controller.runQuery(query); + // create the query + const query = await controller.createQuery({ contract: contractAddress, function: "getSum", arguments: [] }); + // runs the query + const response = await controller.runQuery(query); - // parse the result - const parsedResponse = controller.parseQueryResponse(response); - } + // parse the result + const parsedResponse = controller.parseQueryResponse(response); +} ``` ### Upgrading a smart contract @@ -1327,50 +1317,48 @@ However, in this case, the contract address is already known. Like deploying a s #### Uprgrading a smart contract using the controller ```js - { - // prepare the account - const entrypoint = new DevnetEntrypoint(); - const keystorePath = path.join("../src", "testdata", "testwallets", "alice.json"); - const sender = Account.newFromKeystore(keystorePath, "password"); - // the developer is responsible for managing the nonce - sender.nonce = await entrypoint.recallAccountNonce(sender.address); +{ + // prepare the account + const entrypoint = new DevnetEntrypoint(); + const keystorePath = path.join("../src", "testdata", "testwallets", "alice.json"); + const sender = Account.newFromKeystore(keystorePath, "password"); + // the developer is responsible for managing the nonce + sender.nonce = await entrypoint.recallAccountNonce(sender.address); - // load the abi - const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); + // load the abi + const abi = await loadAbiRegistry("../src/testdata/adder.abi.json"); - // create the controller - const controller = entrypoint.createSmartContractController(abi); + // create the controller + const controller = entrypoint.createSmartContractController(abi); - // load the contract bytecode; this is the new contract code, the one we want to upgrade to - const bytecode = await promises.readFile("../src/testData/adder.wasm"); + // load the contract bytecode; this is the new contract code, the one we want to upgrade to + const bytecode = await promises.readFile("../src/testData/adder.wasm"); - // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: - let args: any[] = [new U32Value(42)]; - // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: - args = [42]; + // For deploy arguments, use "TypedValue" objects if you haven't provided an ABI to the factory: + let args: any[] = [new U32Value(42)]; + // Or use simple, plain JavaScript values and objects if you have provided an ABI to the factory: + args = [42]; - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); + const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgq7cmfueefdqkjsnnjnwydw902v8pwjqy3d8ssd4meug"); - const upgradeTransaction = await controller.createTransactionForUpgrade( - sender, - sender.getNonceThenIncrement(), - { - contract: contractAddress, - bytecode: bytecode, - gasLimit: 6000000n, - arguments: args, - }, - ); + const upgradeTransaction = await controller.createTransactionForUpgrade( + sender, + sender.getNonceThenIncrement(), + { + contract: contractAddress, + bytecode: bytecode, + gasLimit: 6000000n, + arguments: args, + }, + ); - // broadcasting the transaction - const txHash = await entrypoint.sendTransaction(upgradeTransaction); + // broadcasting the transaction + const txHash = await entrypoint.sendTransaction(upgradeTransaction); - console.log({ txHash }); - } + console.log({ txHash }); +} ``` -md-start - ### Token management In this section, we're going to create transactions to issue fungible tokens, issue semi-fungible tokens, create NFTs, set token roles, but also parse these transactions to extract their outcome (e.g. get the token identifier of the newly issued token). @@ -1383,221 +1371,187 @@ For scripts or quick network interactions, we recommend using the controller. Ho #### Issuing fungible tokens using the controller ```js - { - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createTokenManagementController(); +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); - // create the issuer of the token - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const transaction = await controller.createTransactionForIssuingFungible(alice, alice.getNonceThenIncrement(), { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - }); + const transaction = await controller.createTransactionForIssuingFungible(alice, alice.getNonceThenIncrement(), { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); - // wait for transaction to execute, extract the token identifier - const outcome = await controller.awaitCompletedIssueFungible(txHash); + // wait for transaction to execute, extract the token identifier + const outcome = await controller.awaitCompletedIssueFungible(txHash); - const tokenIdentifier = outcome[0].tokenIdentifier; - } + const tokenIdentifier = outcome[0].tokenIdentifier; +} ``` #### Issuing fungible tokens using the factory ```js - { - // create the entrypoint and the token management transactions factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); - - // create the issuer of the token - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const transaction = await factory.createTransactionForIssuingFungible(alice.address, { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier - // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` - const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - - // extract the token identifier - const parser = new TokenManagementTransactionsOutcomeParser(); - const outcome = parser.parseIssueFungible(transactionOnNetwork); - const tokenIdentifier = outcome[0].tokenIdentifier; - } +{ + // create the entrypoint and the token management transactions factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForIssuingFungible(alice.address, { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + // extract the token identifier + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueFungible(transactionOnNetwork); + const tokenIdentifier = outcome[0].tokenIdentifier; +} ``` #### Setting special roles for fungible tokens using the controller ```js - { - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createTokenManagementController(); +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); - // create the issuer of the token - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const bob = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - const transaction = await controller.createTransactionForSettingSpecialRoleOnFungibleToken( - alice, - alice.getNonceThenIncrement(), - { - user: bob, - tokenIdentifier: "TEST-123456", - addRoleLocalMint: true, - addRoleLocalBurn: true, - addRoleESDTTransferRole: true, - }, - ); + const transaction = await controller.createTransactionForSettingSpecialRoleOnFungibleToken( + alice, + alice.getNonceThenIncrement(), + { + user: bob, + tokenIdentifier: "TEST-123456", + addRoleLocalMint: true, + addRoleLocalBurn: true, + addRoleESDTTransferRole: true, + }, + ); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); - // wait for transaction to execute, extract the token identifier - const outcome = await controller.awaitCompletedSetSpecialRoleOnFungibleToken(txHash); + // wait for transaction to execute, extract the token identifier + const outcome = await controller.awaitCompletedSetSpecialRoleOnFungibleToken(txHash); - const roles = outcome[0].roles; - const user = outcome[0].userAddress; - } + const roles = outcome[0].roles; + const user = outcome[0].userAddress; +} ``` #### Setting special roles for fungible tokens using the factory ```js - { - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); - - // create the issuer of the token - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const transaction = await factory.createTransactionForIssuingFungible(alice.address, { - tokenName: "TEST", - tokenTicker: "TEST", - initialSupply: 100n, - numDecimals: 0n, - canFreeze: true, - canWipe: true, - canPause: true, - canChangeOwner: true, - canUpgrade: false, - canAddSpecialRoles: false, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - - // wait for transaction to execute, extract the token identifier - // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` - const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - - const parser = new TokenManagementTransactionsOutcomeParser(); - const outcome = parser.parseSetSpecialRole(transactionOnNetwork); - - const roles = outcome[0].roles; - const user = outcome[0].userAddress; - } +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const transaction = await factory.createTransactionForIssuingFungible(alice.address, { + tokenName: "TEST", + tokenTicker: "TEST", + initialSupply: 100n, + numDecimals: 0n, + canFreeze: true, + canWipe: true, + canPause: true, + canChangeOwner: true, + canUpgrade: false, + canAddSpecialRoles: false, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); + + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + // if we know that the transaction is completed, we can simply call `entrypoint.get_transaction(tx_hash)` + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseSetSpecialRole(transactionOnNetwork); + + const roles = outcome[0].roles; + const user = outcome[0].userAddress; +} ``` #### Issuing semi-fungible tokens using the controller ```js - { - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createTokenManagementController(); - - // create the issuer of the token - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - const transaction = await controller.createTransactionForIssuingSemiFungible( - alice, - alice.getNonceThenIncrement(), - { - tokenName: "NEWSEMI", - tokenTicker: "SEMI", - canFreeze: false, - canWipe: true, - canPause: false, - canTransferNFTCreateRole: true, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: true, - }, - ); - - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); - // wait for transaction to execute, extract the token identifier - const outcome = await controller.awaitCompletedIssueSemiFungible(txHash); + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const tokenIdentifier = outcome[0].tokenIdentifier; - } -``` + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); -#### Issuing semi-fungible tokens using the factory -```js - { - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); - - // create the issuer of the token - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - const transaction = await factory.createTransactionForIssuingSemiFungible(alice.address, { + const transaction = await controller.createTransactionForIssuingSemiFungible( + alice, + alice.getNonceThenIncrement(), + { tokenName: "NEWSEMI", tokenTicker: "SEMI", canFreeze: false, @@ -1607,102 +1561,81 @@ For scripts or quick network interactions, we recommend using the controller. Ho canChangeOwner: true, canUpgrade: true, canAddSpecialRoles: true, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); - - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); - - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); + }, + ); - // wait for transaction to execute, extract the token identifier - const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); - // extract the token identifier - const parser = new TokenManagementTransactionsOutcomeParser(); - const outcome = parser.parseIssueSemiFungible(transactionOnNetwork); + // wait for transaction to execute, extract the token identifier + const outcome = await controller.awaitCompletedIssueSemiFungible(txHash); - const tokenIdentifier = outcome[0].tokenIdentifier; - } + const tokenIdentifier = outcome[0].tokenIdentifier; +} ``` -#### Issuing NFT collection & creating NFTs using the controller - +#### Issuing semi-fungible tokens using the factory ```js - { - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createTokenManagementController(); - - // create the issuer of the token - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - - let transaction = await controller.createTransactionForIssuingNonFungible( - alice, - alice.getNonceThenIncrement(), - { - tokenName: "NEWNFT", - tokenTicker: "NFT", - canFreeze: false, - canWipe: true, - canPause: false, - canTransferNFTCreateRole: true, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: true, - }, - ); +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); - // sending the transaction - let txHash = await entrypoint.sendTransaction(transaction); + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // wait for transaction to execute, extract the token identifier - const outcome = await controller.awaitCompletedIssueNonFungible(txHash); + const transaction = await factory.createTransactionForIssuingSemiFungible(alice.address, { + tokenName: "NEWSEMI", + tokenTicker: "SEMI", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); - const collectionIdentifier = outcome[0].tokenIdentifier; + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // create an NFT - transaction = await controller.createTransactionForCreatingNft(alice, alice.getNonceThenIncrement(), { - tokenIdentifier: "FRANK-aa9e8d", - initialQuantity: 1n, - name: "test", - royalties: 1000, - hash: "abba", - attributes: Buffer.from("test"), - uris: ["a", "b"], - }); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); - // sending the transaction - txHash = await entrypoint.sendTransaction(transaction); + // wait for transaction to execute, extract the token identifier + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - // wait for transaction to execute, extract the token identifier - const outcomeNft = await controller.awaitCompletedCreateNft(txHash); + // extract the token identifier + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueSemiFungible(transactionOnNetwork); - const identifier = outcomeNft[0].tokenIdentifier; - const nonce = outcomeNft[0].nonce; - const initialQuantity = outcomeNft[0].initialQuantity; - } + const tokenIdentifier = outcome[0].tokenIdentifier; +} ``` -#### Issuing NFT collection & creating NFTs using the factory +#### Issuing NFT collection & creating NFTs using the controller + ```js - { - // create the entrypoint and the token management transdactions factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); + + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // create the issuer of the token - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - let transaction = await factory.createTransactionForIssuingNonFungible(alice.address, { + let transaction = await controller.createTransactionForIssuingNonFungible( + alice, + alice.getNonceThenIncrement(), + { tokenName: "NEWNFT", tokenTicker: "NFT", canFreeze: false, @@ -1712,51 +1645,106 @@ For scripts or quick network interactions, we recommend using the controller. Ho canChangeOwner: true, canUpgrade: true, canAddSpecialRoles: true, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); + }, + ); + + // sending the transaction + let txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + const outcome = await controller.awaitCompletedIssueNonFungible(txHash); + + const collectionIdentifier = outcome[0].tokenIdentifier; + + // create an NFT + transaction = await controller.createTransactionForCreatingNft(alice, alice.getNonceThenIncrement(), { + tokenIdentifier: "FRANK-aa9e8d", + initialQuantity: 1n, + name: "test", + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + }); + + // sending the transaction + txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction to execute, extract the token identifier + const outcomeNft = await controller.awaitCompletedCreateNft(txHash); + + const identifier = outcomeNft[0].tokenIdentifier; + const nonce = outcomeNft[0].nonce; + const initialQuantity = outcomeNft[0].initialQuantity; +} +``` + +#### Issuing NFT collection & creating NFTs using the factory +```js +{ + // create the entrypoint and the token management transdactions factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); + + // create the issuer of the token + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + let transaction = await factory.createTransactionForIssuingNonFungible(alice.address, { + tokenName: "NEWNFT", + tokenTicker: "NFT", + canFreeze: false, + canWipe: true, + canPause: false, + canTransferNFTCreateRole: true, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: true, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - let txHash = await entrypoint.sendTransaction(transaction); + // sending the transaction + let txHash = await entrypoint.sendTransaction(transaction); - // wait for transaction to execute, extract the token identifier - let transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + // wait for transaction to execute, extract the token identifier + let transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - // extract the token identifier - let parser = new TokenManagementTransactionsOutcomeParser(); - let outcome = parser.parseIssueNonFungible(transactionOnNetwork); + // extract the token identifier + let parser = new TokenManagementTransactionsOutcomeParser(); + let outcome = parser.parseIssueNonFungible(transactionOnNetwork); - const collectionIdentifier = outcome[0].tokenIdentifier; + const collectionIdentifier = outcome[0].tokenIdentifier; - transaction = await factory.createTransactionForCreatingNFT(alice.address, { - tokenIdentifier: "FRANK-aa9e8d", - initialQuantity: 1n, - name: "test", - royalties: 1000, - hash: "abba", - attributes: Buffer.from("test"), - uris: ["a", "b"], - }); + transaction = await factory.createTransactionForCreatingNFT(alice.address, { + tokenIdentifier: "FRANK-aa9e8d", + initialQuantity: 1n, + name: "test", + royalties: 1000, + hash: "abba", + attributes: Buffer.from("test"), + uris: ["a", "b"], + }); - transaction.nonce = alice.getNonceThenIncrement(); + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - txHash = await entrypoint.sendTransaction(transaction); + // sending the transaction + txHash = await entrypoint.sendTransaction(transaction); - // ### wait for transaction to execute, extract the token identifier - transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + // ### wait for transaction to execute, extract the token identifier + transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - outcome = parser.parseIssueNonFungible(transactionOnNetwork); + outcome = parser.parseIssueNonFungible(transactionOnNetwork); - const identifier = outcome[0].tokenIdentifier; - } + const identifier = outcome[0].tokenIdentifier; +} ``` These are just a few examples of what you can do using the token management controller or factory. For a complete list of supported methods, please refer to the autogenerated documentation: @@ -1764,8 +1752,6 @@ These are just a few examples of what you can do using the token management cont - [TokenManagementController](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementController.html) - [TokenManagementTransactionsFactory](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TokenManagementTransactionsFactory.html) -md-start - ### Account management The account management controller and factory allow us to create transactions for managing accounts, such as: @@ -1777,230 +1763,228 @@ A guardian can also be set using the WebWallet, which leverages our hosted `Trus #### Guarding an account using the controller ```js - { - // create the entrypoint and the account controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createAccountController(); +{ + // create the entrypoint and the account controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); - // create the account to guard - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // create the account to guard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // we can use a trusted service that provides a guardian, or simply set another address we own or trust - const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + // we can use a trusted service that provides a guardian, or simply set another address we own or trust + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - const transaction = await controller.createTransactionForSettingGuardian(alice, alice.getNonceThenIncrement(), { - guardianAddress: guardian, - serviceID: "SelfOwnedAddress", // this is just an example - }); + const transaction = await controller.createTransactionForSettingGuardian(alice, alice.getNonceThenIncrement(), { + guardianAddress: guardian, + serviceID: "SelfOwnedAddress", // this is just an example + }); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Guarding an account using the factory ```js - { - // create the entrypoint and the account management factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createAccountTransactionsFactory(); +{ + // create the entrypoint and the account management factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); - // create the account to guard - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // create the account to guard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // we can use a trusted service that provides a guardian, or simply set another address we own or trust - const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + // we can use a trusted service that provides a guardian, or simply set another address we own or trust + const guardian = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - const transaction = await factory.createTransactionForSettingGuardian(alice.address, { - guardianAddress: guardian, - serviceID: "SelfOwnedAddress", // this is just an example - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const transaction = await factory.createTransactionForSettingGuardian(alice.address, { + guardianAddress: guardian, + serviceID: "SelfOwnedAddress", // this is just an example + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` Once a guardian is set, we must wait **20 epochs** before it can be activated. After activation, all transactions sent from the account must also be signed by the guardian. #### Activating the guardian using the controller ```js - { - // create the entrypoint and the account controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createAccountController(); +{ + // create the entrypoint and the account controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); - // create the account to guard - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // create the account to guard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const transaction = await controller.createTransactionForGuardingAccount( - alice, - alice.getNonceThenIncrement(), - {}, - ); + const transaction = await controller.createTransactionForGuardingAccount( + alice, + alice.getNonceThenIncrement(), + {}, + ); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Activating the guardian using the factory ```js - { - // create the entrypoint and the account factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createAccountTransactionsFactory(); +{ + // create the entrypoint and the account factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); - // create the account to guard - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // create the account to guard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const transaction = await factory.createTransactionForGuardingAccount(alice.address); + const transaction = await factory.createTransactionForGuardingAccount(alice.address); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Unguarding the account using the controller ```js - { - // create the entrypoint and the account controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createAccountController(); +{ + // create the entrypoint and the account controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); - // create the account to unguard - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // create the account to unguard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const transaction = await controller.createTransactionForUnguardingAccount( - alice, - alice.getNonceThenIncrement(), - {}, - ); + const transaction = await controller.createTransactionForUnguardingAccount( + alice, + alice.getNonceThenIncrement(), + {}, + ); - // the transaction should also be signed by the guardian before being sent otherwise it won't be executed - const txHash = await entrypoint.sendTransaction(transaction); - } + // the transaction should also be signed by the guardian before being sent otherwise it won't be executed + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Unguarding the guardian using the factory ```js - { - // create the entrypoint and the account factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createAccountTransactionsFactory(); +{ + // create the entrypoint and the account factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); - // create the account to guard - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // create the account to guard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const transaction = await factory.createTransactionForUnguardingAccount(alice.address); + const transaction = await factory.createTransactionForUnguardingAccount(alice.address); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Saving a key-value pair to an account using the controller You can find more information [here](/developers/account-storage) regarding the account storage. ```js - { - // create the entrypoint and the account controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createAccountController(); +{ + // create the entrypoint and the account controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createAccountController(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // creating the key-value pairs we want to save - const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); + // creating the key-value pairs we want to save + const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const transaction = await controller.createTransactionForSavingKeyValue(alice, alice.getNonceThenIncrement(), { - keyValuePairs: keyValuePairs, - }); + const transaction = await controller.createTransactionForSavingKeyValue(alice, alice.getNonceThenIncrement(), { + keyValuePairs: keyValuePairs, + }); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Saving a key-value pair to an account using the factory ```js - { - // create the entrypoint and the account factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createAccountTransactionsFactory(); +{ + // create the entrypoint and the account factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createAccountTransactionsFactory(); - // create the account to guard - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + // create the account to guard + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // creating the key-value pairs we want to save - const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); + // creating the key-value pairs we want to save + const keyValuePairs = new Map([[Buffer.from("key0"), Buffer.from("value0")]]); - const transaction = await factory.createTransactionForSavingKeyValue(alice.address, { - keyValuePairs: keyValuePairs, - }); + const transaction = await factory.createTransactionForSavingKeyValue(alice.address, { + keyValuePairs: keyValuePairs, + }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` -md-start - ### Delegation management To learn more about staking providers and delegation, please refer to the official [documentation](/validators/delegation-manager/#introducing-staking-providers). @@ -2018,356 +2002,354 @@ These operations can be performed using both the controller and the **factory**. #### Creating a New Delegation Contract Using the Controller ```js - { - // create the entrypoint and the delegation controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); +{ + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const transaction = await controller.createTransactionForNewDelegationContract( - alice, - alice.getNonceThenIncrement(), - { - totalDelegationCap: 0n, - serviceFee: 10n, - amount: 1250000000000000000000n, - }, - ); + const transaction = await controller.createTransactionForNewDelegationContract( + alice, + alice.getNonceThenIncrement(), + { + totalDelegationCap: 0n, + serviceFee: 10n, + amount: 1250000000000000000000n, + }, + ); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); - // wait for transaction completion, extract delegation contract's address - const outcome = await controller.awaitCompletedCreateNewDelegationContract(txHash); + // wait for transaction completion, extract delegation contract's address + const outcome = await controller.awaitCompletedCreateNewDelegationContract(txHash); - const contractAddress = outcome[0].contractAddress; - } + const contractAddress = outcome[0].contractAddress; +} ``` #### Creating a new delegation contract using the factory ```js - { - // create the entrypoint and the delegation factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); +{ + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const transaction = await factory.createTransactionForNewDelegationContract(alice.address, { - totalDelegationCap: 0n, - serviceFee: 10n, - amount: 1250000000000000000000n, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const transaction = await factory.createTransactionForNewDelegationContract(alice.address, { + totalDelegationCap: 0n, + serviceFee: 10n, + amount: 1250000000000000000000n, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); - // waits until the transaction is processed and fetches it from the network - const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); + // waits until the transaction is processed and fetches it from the network + const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash); - // extract the token identifier - const parser = new TokenManagementTransactionsOutcomeParser(); - const outcome = parser.parseIssueFungible(transactionOnNetwork); - const tokenIdentifier = outcome[0].tokenIdentifier; - } + // extract the token identifier + const parser = new TokenManagementTransactionsOutcomeParser(); + const outcome = parser.parseIssueFungible(transactionOnNetwork); + const tokenIdentifier = outcome[0].tokenIdentifier; +} ``` #### Delegating funds to the contract using the Controller We can send funds to a delegation contract to earn rewards. ```js - { - // create the entrypoint and the delegation controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); +{ + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - const transaction = await controller.createTransactionForDelegating(alice, alice.getNonceThenIncrement(), { - delegationContract: contract, - amount: 5000000000000000000000n, - }); + const transaction = await controller.createTransactionForDelegating(alice, alice.getNonceThenIncrement(), { + delegationContract: contract, + amount: 5000000000000000000000n, + }); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Delegating funds to the contract using the factory ```js - { - // create the entrypoint and the delegation factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); +{ + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - const transaction = await factory.createTransactionForDelegating(alice.address, { - delegationContract: contract, - amount: 5000000000000000000000n, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const transaction = await factory.createTransactionForDelegating(alice.address, { + delegationContract: contract, + amount: 5000000000000000000000n, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Redelegating rewards using the Controller Over time, as rewards accumulate, we may choose to redelegate them to the contract to maximize earnings. ```js - { - // create the entrypoint and the delegation controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); +{ + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const transaction = await controller.createTransactionForRedelegatingRewards( - alice, - alice.getNonceThenIncrement(), - { - delegationContract: contract, - }, - ); + const transaction = await controller.createTransactionForRedelegatingRewards( + alice, + alice.getNonceThenIncrement(), + { + delegationContract: contract, + }, + ); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Redelegating rewards using the factory ```js - { - // create the entrypoint and the delegation factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); +{ + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - const transaction = await factory.createTransactionForRedelegatingRewards(alice.address, { - delegationContract: contract, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const transaction = await factory.createTransactionForRedelegatingRewards(alice.address, { + delegationContract: contract, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Claiming rewards using the Controller We can also claim our rewards when needed. ```js - { - // create the entrypoint and the delegation controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); +{ + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const transaction = await controller.createTransactionForClaimingRewards(alice, alice.getNonceThenIncrement(), { - delegationContract: contract, - }); + const transaction = await controller.createTransactionForClaimingRewards(alice, alice.getNonceThenIncrement(), { + delegationContract: contract, + }); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Claiming rewards using the factory ```js - { - // create the entrypoint and the delegation factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); +{ + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - const transaction = await factory.createTransactionForClaimingRewards(alice.address, { - delegationContract: contract, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const transaction = await factory.createTransactionForClaimingRewards(alice.address, { + delegationContract: contract, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Undelegating funds using the Controller By **undelegating**, we signal the contract that we want to retrieve our staked funds. This process requires a **10-epoch unbonding period** before the funds become available. ```js - { - // create the entrypoint and the delegation controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); +{ + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const transaction = await controller.createTransactionForUndelegating(alice, alice.getNonceThenIncrement(), { - delegationContract: contract, - amount: 1000000000000000000000n, // 1000 EGLD - }); + const transaction = await controller.createTransactionForUndelegating(alice, alice.getNonceThenIncrement(), { + delegationContract: contract, + amount: 1000000000000000000000n, // 1000 EGLD + }); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Undelegating funds using the factory ```js - { - // create the entrypoint and the delegation factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); +{ + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - const transaction = await factory.createTransactionForUndelegating(alice.address, { - delegationContract: contract, - amount: 1000000000000000000000n, // 1000 EGLD - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const transaction = await factory.createTransactionForUndelegating(alice.address, { + delegationContract: contract, + amount: 1000000000000000000000n, // 1000 EGLD + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Withdrawing funds using the Controller After the `10-epoch unbonding period` is complete, we can proceed with withdrawing our staked funds using the controller. This final step allows us to regain access to the previously delegated funds. ```js - { - // create the entrypoint and the delegation controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createDelegationController(); +{ + // create the entrypoint and the delegation controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createDelegationController(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const transaction = await controller.createTransactionForWithdrawing(alice, alice.getNonceThenIncrement(), { - delegationContract: contract, - }); + const transaction = await controller.createTransactionForWithdrawing(alice, alice.getNonceThenIncrement(), { + delegationContract: contract, + }); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Withdrawing funds using the factory ```js - { - // create the entrypoint and the delegation factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createDelegationTransactionsFactory(); +{ + // create the entrypoint and the delegation factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createDelegationTransactionsFactory(); - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - const transaction = await factory.createTransactionForWithdrawing(alice.address, { - delegationContract: contract, - }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + const transaction = await factory.createTransactionForWithdrawing(alice.address, { + delegationContract: contract, + }); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - // set the nonce - transaction.nonce = alice.getNonceThenIncrement(); + // set the nonce + transaction.nonce = alice.getNonceThenIncrement(); - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // sending the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` -md-start - ### Relayed transactions We are currently on the `third iteration (V3)` of relayed transactions. V1 and V2 will soon be deactivated, so we will focus on V3. @@ -2384,35 +2366,35 @@ Relayed V3 transactions require an additional `50,000` gas. Let’s see how to create a relayed transaction: ```js - { - const entrypoint = new DevnetEntrypoint(); - const walletsPath = path.join("../src", "testdata", "testwallets"); - const bob = await Account.newFromPem(path.join(walletsPath, "bob.pem")); - const grace = Address.newFromBech32("erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede"); - const mike = await Account.newFromPem(path.join(walletsPath, "mike.pem")); +{ + const entrypoint = new DevnetEntrypoint(); + const walletsPath = path.join("../src", "testdata", "testwallets"); + const bob = await Account.newFromPem(path.join(walletsPath, "bob.pem")); + const grace = Address.newFromBech32("erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede"); + const mike = await Account.newFromPem(path.join(walletsPath, "mike.pem")); - // fetch the nonce of the network - bob.nonce = await entrypoint.recallAccountNonce(bob.address); + // fetch the nonce of the network + bob.nonce = await entrypoint.recallAccountNonce(bob.address); - const transaction = new Transaction({ - chainID: "D", - sender: bob.address, - receiver: grace, - relayer: mike.address, - gasLimit: 110_000n, - data: Buffer.from("hello"), - nonce: bob.getNonceThenIncrement(), - }); + const transaction = new Transaction({ + chainID: "D", + sender: bob.address, + receiver: grace, + relayer: mike.address, + gasLimit: 110_000n, + data: Buffer.from("hello"), + nonce: bob.getNonceThenIncrement(), + }); - // sender signs the transaction - transaction.signature = await bob.signTransaction(transaction); + // sender signs the transaction + transaction.signature = await bob.signTransaction(transaction); - // relayer signs the transaction - transaction.relayerSignature = await mike.signTransaction(transaction); + // relayer signs the transaction + transaction.relayerSignature = await mike.signTransaction(transaction); - // broadcast the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Creating relayed transactions using controllers @@ -2422,41 +2404,41 @@ Each controller includes a relayer argument, which must be set if we want to cre Let’s issue a fungible token using a relayed transaction: ```js - { - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createTokenManagementController(); +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); - // create the issuer of the token - const walletsPath = path.join("../src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + // create the issuer of the token + const walletsPath = path.join("../src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); - // Carol will be our relayer, that means she is paying the gas for the transaction - const frank = await Account.newFromPem(path.join(walletsPath, "frank.pem")); + // Carol will be our relayer, that means she is paying the gas for the transaction + const frank = await Account.newFromPem(path.join(walletsPath, "frank.pem")); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const transaction = await controller.createTransactionForIssuingFungible(alice, alice.getNonceThenIncrement(), { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - relayer: frank.address, - }); + const transaction = await controller.createTransactionForIssuingFungible(alice, alice.getNonceThenIncrement(), { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + relayer: frank.address, + }); - // relayer also signs the transaction - transaction.relayerSignature = await frank.signTransaction(transaction); + // relayer also signs the transaction + transaction.relayerSignature = await frank.signTransaction(transaction); - // broadcast the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Creating relayed transactions using factories @@ -2466,51 +2448,49 @@ This approach is beneficial because the **transaction is not signed by the sende Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: ```js - { - // create the entrypoint and the token management factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); +{ + // create the entrypoint and the token management factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); - // create the issuer of the token - const walletsPath = path.join("../src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + // create the issuer of the token + const walletsPath = path.join("../src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); - // carol will be our relayer, that means she is paying the gas for the transaction - const frank = await Account.newFromPem(path.join(walletsPath, "frank.pem")); + // carol will be our relayer, that means she is paying the gas for the transaction + const frank = await Account.newFromPem(path.join(walletsPath, "frank.pem")); - const transaction = await factory.createTransactionForIssuingFungible(alice.address, { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - }); + const transaction = await factory.createTransactionForIssuingFungible(alice.address, { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); - // set the relayer - transaction.relayer = frank.address; + // set the relayer + transaction.relayer = frank.address; - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // relayer also signs the transaction - transaction.relayerSignature = await frank.signTransaction(transaction); + // relayer also signs the transaction + transaction.relayerSignature = await frank.signTransaction(transaction); - // broadcast the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` -md-start - ### Guarded transactions Similar to relayers, transactions also have two additional fields: @@ -2530,41 +2510,41 @@ Each controller method includes a guardian argument, which must be set if we wan Let’s issue a fungible token using a relayed transaction: ```js - { - // create the entrypoint and the token management controller - const entrypoint = new DevnetEntrypoint(); - const controller = entrypoint.createTokenManagementController(); +{ + // create the entrypoint and the token management controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createTokenManagementController(); - // create the issuer of the token - const walletsPath = path.join("../src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + // create the issuer of the token + const walletsPath = path.join("../src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); - // carol will be our guardian - const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + // carol will be our guardian + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); - const transaction = await controller.createTransactionForIssuingFungible(alice, alice.getNonceThenIncrement(), { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - guardian: carol.address, - }); + const transaction = await controller.createTransactionForIssuingFungible(alice, alice.getNonceThenIncrement(), { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + guardian: carol.address, + }); - // guardian also signs the transaction - transaction.guardianSignature = await carol.signTransaction(transaction); + // guardian also signs the transaction + transaction.guardianSignature = await carol.signTransaction(transaction); - // broadcast the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` #### Creating guarded transactions using factories @@ -2574,47 +2554,47 @@ This approach is beneficial because the transaction is **not signed by the sende Let’s issue a fungible token using the `TokenManagementTransactionsFactory`: ```js - { - // create the entrypoint and the token management factory - const entrypoint = new DevnetEntrypoint(); - const factory = entrypoint.createTokenManagementTransactionsFactory(); +{ + // create the entrypoint and the token management factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createTokenManagementTransactionsFactory(); - // create the issuer of the token - const walletsPath = path.join("../src", "testdata", "testwallets"); - const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); + // create the issuer of the token + const walletsPath = path.join("../src", "testdata", "testwallets"); + const alice = await Account.newFromPem(path.join(walletsPath, "alice.pem")); - // carol will be our guardian - const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); + // carol will be our guardian + const carol = await Account.newFromPem(path.join(walletsPath, "carol.pem")); - const transaction = await factory.createTransactionForIssuingFungible(alice.address, { - tokenName: "NEWFNG", - tokenTicker: "FNG", - initialSupply: 1_000_000_000000n, - numDecimals: 6n, - canFreeze: false, - canWipe: true, - canPause: false, - canChangeOwner: true, - canUpgrade: true, - canAddSpecialRoles: false, - }); + const transaction = await factory.createTransactionForIssuingFungible(alice.address, { + tokenName: "NEWFNG", + tokenTicker: "FNG", + initialSupply: 1_000_000_000000n, + numDecimals: 6n, + canFreeze: false, + canWipe: true, + canPause: false, + canChangeOwner: true, + canUpgrade: true, + canAddSpecialRoles: false, + }); - // fetch the nonce of the network - alice.nonce = await entrypoint.recallAccountNonce(alice.address); - transaction.nonce = alice.getNonceThenIncrement(); + // fetch the nonce of the network + alice.nonce = await entrypoint.recallAccountNonce(alice.address); + transaction.nonce = alice.getNonceThenIncrement(); - // set the guardian - transaction.guardian = carol.address; + // set the guardian + transaction.guardian = carol.address; - // sign the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign the transaction + transaction.signature = await alice.signTransaction(transaction); - // guardian also signs the transaction - transaction.guardianSignature = await carol.signTransaction(transaction); + // guardian also signs the transaction + transaction.guardianSignature = await carol.signTransaction(transaction); - // broadcast the transaction - const txHash = await entrypoint.sendTransaction(transaction); - } + // broadcast the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} ``` We can create guarded relayed transactions just like we did before. However, keep in mind: @@ -2633,21 +2613,19 @@ Flow for Creating Guarded Relayed Transactions: 4. Then, the guardian signs. 5. Finally, the relayer signs before broadcasting. -md-start - ## Addresses Create an `Address` object from a bech32-encoded string: ``` js - { - // Create an Address object from a bech32-encoded string - const address = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); +{ + // Create an Address object from a bech32-encoded string + const address = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - console.log("Address (bech32-encoded):", address.toBech32()); - console.log("Public key (hex-encoded):", address.toHex()); - console.log("Public key (hex-encoded):", Buffer.from(address.getPublicKey()).toString("hex")); - } + console.log("Address (bech32-encoded):", address.toBech32()); + console.log("Public key (hex-encoded):", address.toHex()); + console.log("Public key (hex-encoded):", Buffer.from(address.getPublicKey()).toString("hex")); +} ``` @@ -2655,37 +2633,37 @@ Here’s how you can create an address from a hex-encoded string using the Multi If the HRP (human-readable part) is not provided, the SDK will use the default one ("erd"). ``` js - { - // Create an address from a hex-encoded string with a specified HRP - const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "erd"); +{ + // Create an address from a hex-encoded string with a specified HRP + const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "erd"); - console.log("Address (bech32-encoded):", address.toBech32()); - console.log("Public key (hex-encoded):", address.toHex()); - } + console.log("Address (bech32-encoded):", address.toBech32()); + console.log("Public key (hex-encoded):", address.toHex()); +} ``` #### Create an address from a raw public key ``` js - { - const pubkey = Buffer.from("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "hex"); - const addressFromPubkey = new Address(pubkey, "erd"); - } +{ + const pubkey = Buffer.from("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", "hex"); + const addressFromPubkey = new Address(pubkey, "erd"); +} ``` #### Getting the shard of an address ``` js - const addressComputer = new AddressComputer(); - const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); - console.log("Shard:", addressComputer.getShardOfAddress(address)); +const addressComputer = new AddressComputer(); +const address = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); +console.log("Shard:", addressComputer.getShardOfAddress(address)); ``` Checking if an address is a smart contract ``` js - const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm"); - console.log("Is contract address:", contractAddress.isSmartContract()); +const contractAddress = Address.newFromBech32("erd1qqqqqqqqqqqqqpgquzmh78klkqwt0p4rjys0qtp3la07gz4d396qn50nnm"); +console.log("Is contract address:", contractAddress.isSmartContract()); ``` ### Changing the default hrp @@ -2693,20 +2671,18 @@ The **LibraryConfig** class manages the default **HRP** (human-readable part) fo You can change the HRP when creating an address or modify it globally in **LibraryConfig**, affecting all newly created addresses. ``` js - console.log(LibraryConfig.DefaultAddressHrp); - const defaultAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); - console.log(defaultAddress.toBech32()); +console.log(LibraryConfig.DefaultAddressHrp); +const defaultAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); +console.log(defaultAddress.toBech32()); - LibraryConfig.DefaultAddressHrp = "test"; - const testAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); - console.log(testAddress.toBech32()); +LibraryConfig.DefaultAddressHrp = "test"; +const testAddress = Address.newFromHex("0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"); +console.log(testAddress.toBech32()); - // Reset HRP back to "erd" to avoid affecting other parts of the application. - LibraryConfig.DefaultAddressHrp = "erd"; +// Reset HRP back to "erd" to avoid affecting other parts of the application. +LibraryConfig.DefaultAddressHrp = "erd"; ``` -md-start - ## Wallets #### Generating a mnemonic @@ -2714,141 +2690,137 @@ Mnemonic generation is based on [bip39](https://www.npmjs.com/package/bip39) and ``` js - const mnemonic = Mnemonic.generate(); - const words = mnemonic.getWords(); - console.log({ words }); +const mnemonic = Mnemonic.generate(); +const words = mnemonic.getWords(); ``` #### Saving the mnemonic to a keystore file The mnemonic can be saved to a keystore file: ``` js - { - const mnemonic = Mnemonic.generate(); +{ + const mnemonic = Mnemonic.generate(); - // saves the mnemonic to a keystore file with kind=mnemonic - const wallet = UserWallet.fromMnemonic({ mnemonic: mnemonic.toString(), password: "password" }); + // saves the mnemonic to a keystore file with kind=mnemonic + const wallet = UserWallet.fromMnemonic({ mnemonic: mnemonic.toString(), password: "password" }); - const filePath = path.join("../src", "testdata", "testwallets", "walletWithMnemonic.json"); - console.log({ filePath }); - wallet.save(filePath); - } + const filePath = path.join("../src", "testdata", "testwallets", "walletWithMnemonic.json"); + wallet.save(filePath); +} ``` #### Deriving secret keys from a mnemonic Given a mnemonic, we can derive keypairs: ``` js - { - const mnemonic = Mnemonic.generate(); +{ + const mnemonic = Mnemonic.generate(); - const secretKey = mnemonic.deriveKey(0); - const publicKey = secretKey.generatePublicKey(); + const secretKey = mnemonic.deriveKey(0); + const publicKey = secretKey.generatePublicKey(); - console.log("Secret key: ", secretKey.hex()); - console.log("Public key: ", publicKey.hex()); - } + console.log("Secret key: ", secretKey.hex()); + console.log("Public key: ", publicKey.hex()); +} ``` #### Saving a secret key to a keystore file The secret key can also be saved to a keystore file: ``` js - { - const mnemonic = Mnemonic.generate(); - const secretKey = mnemonic.deriveKey(); +{ + const mnemonic = Mnemonic.generate(); + const secretKey = mnemonic.deriveKey(); - const wallet = UserWallet.fromSecretKey({ secretKey: secretKey, password: "password" }); + const wallet = UserWallet.fromSecretKey({ secretKey: secretKey, password: "password" }); - const filePath = path.join("../src", "testdata", "testwallets", "walletWithSecretKey.json"); - wallet.save(filePath); - } + const filePath = path.join("../src", "testdata", "testwallets", "walletWithSecretKey.json"); + wallet.save(filePath); +} ``` #### Saving a secret key to a PEM file We can save a secret key to a pem file. *This is not recommended as it is not secure, but it's very convenient for testing purposes.* ``` js - { - const mnemonic = Mnemonic.generate(); +{ + const mnemonic = Mnemonic.generate(); - // by default, derives using the index = 0 - const secretKey = mnemonic.deriveKey(); - const publicKey = secretKey.generatePublicKey(); + // by default, derives using the index = 0 + const secretKey = mnemonic.deriveKey(); + const publicKey = secretKey.generatePublicKey(); - const label = publicKey.toAddress().toBech32(); - const pem = new UserPem(label, secretKey); + const label = publicKey.toAddress().toBech32(); + const pem = new UserPem(label, secretKey); - const filePath = path.join("../src", "testdata", "testwallets", "wallet.pem"); - pem.save(filePath); - } + const filePath = path.join("../src", "testdata", "testwallets", "wallet.pem"); + pem.save(filePath); +} ``` #### Generating a KeyPair A `KeyPair` is a wrapper over a secret key and a public key. We can create a keypair and use it for signing or verifying. ``` js - { - const keypair = KeyPair.generate(); +{ + const keypair = KeyPair.generate(); - // by default, derives using the index = 0 - const secretKey = keypair.getSecretKey(); - const publicKey = keypair.getPublicKey(); - } + // by default, derives using the index = 0 + const secretKey = keypair.getSecretKey(); + const publicKey = keypair.getPublicKey(); +} ``` #### Loading a wallet from keystore mnemonic file Load a keystore that holds an encrypted mnemonic (and perform wallet derivation at the same time): ``` js - { - const filePath = path.join("../src", "testdata", "testwallets", "walletWithMnemonic.json"); +{ + const filePath = path.join("../src", "testdata", "testwallets", "walletWithMnemonic.json"); - // loads the mnemonic and derives the a secret key; default index = 0 - let secretKey = UserWallet.loadSecretKey(filePath, "password"); - let address = secretKey.generatePublicKey().toAddress("erd"); + // loads the mnemonic and derives the a secret key; default index = 0 + let secretKey = UserWallet.loadSecretKey(filePath, "password"); + let address = secretKey.generatePublicKey().toAddress("erd"); - console.log("Secret key: ", secretKey.hex()); - console.log("Address: ", address.toBech32()); + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); - // derive secret key with index = 7 - secretKey = UserWallet.loadSecretKey(filePath, "password", 7); - address = secretKey.generatePublicKey().toAddress(); + // derive secret key with index = 7 + secretKey = UserWallet.loadSecretKey(filePath, "password", 7); + address = secretKey.generatePublicKey().toAddress(); - console.log("Secret key: ", secretKey.hex()); - console.log("Address: ", address.toBech32()); - } + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); +} ``` #### Loading a wallet from a keystore secret key file ``` js - { - const filePath = path.join("../src", "testdata", "testwallets", "walletWithSecretKey.json"); +{ + const filePath = path.join("../src", "testdata", "testwallets", "walletWithSecretKey.json"); - let secretKey = UserWallet.loadSecretKey(filePath, "password"); - let address = secretKey.generatePublicKey().toAddress("erd"); + let secretKey = UserWallet.loadSecretKey(filePath, "password"); + let address = secretKey.generatePublicKey().toAddress("erd"); - console.log("Secret key: ", secretKey.hex()); - console.log("Address: ", address.toBech32()); - } + console.log("Secret key: ", secretKey.hex()); + console.log("Address: ", address.toBech32()); +} ``` #### Loading a wallet from a PEM file ``` js - { - const filePath = path.join("../src", "testdata", "testwallets", "wallet.pem"); +{ + const filePath = path.join("../src", "testdata", "testwallets", "wallet.pem"); - let pem = UserPem.fromFile(filePath); + let pem = UserPem.fromFile(filePath); - console.log("Secret key: ", pem.secretKey.hex()); - console.log("Public key: ", pem.publicKey.hex()); - } + console.log("Secret key: ", pem.secretKey.hex()); + console.log("Public key: ", pem.publicKey.hex()); +} ``` -md-start - ## Signing objects Signing is done using an account's secret key. To simplify this process, we provide wrappers like [Account](#creating-accounts), which streamline signing operations. @@ -2857,116 +2829,114 @@ First, we'll explore how to sign using an Account, followed by signing directly #### Signing a Transaction using an Account We are going to assume we have an account at this point. If you don't, feel free to check out the [creating an account](#creating-accounts) section. ```js - { - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); +{ + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const transaction = new Transaction({ - chainID: "D", - sender: alice.address, - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - gasLimit: 50000n, - nonce: 90n, - }); + const transaction = new Transaction({ + chainID: "D", + sender: alice.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + gasLimit: 50000n, + nonce: 90n, + }); - transaction.signature = await alice.signTransaction(transaction); - console.log(transaction.toPlainObject()); - } + transaction.signature = await alice.signTransaction(transaction); + console.log(transaction.toPlainObject()); +} ``` #### Signing a Transaction using a SecretKey ```js - { - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = UserSecretKey.fromString(secretKeyHex); - const publickKey = secretKey.generatePublicKey(); +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = UserSecretKey.fromString(secretKeyHex); + const publickKey = secretKey.generatePublicKey(); - const transaction = new Transaction({ - nonce: 90n, - sender: publickKey.toAddress(), - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - value: 1000000000000000000n, - gasLimit: 50000n, - chainID: "D", - }); + const transaction = new Transaction({ + nonce: 90n, + sender: publickKey.toAddress(), + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D", + }); - // serialize the transaction - const transactionComputer = new TransactionComputer(); - const serializedTransaction = transactionComputer.computeBytesForSigning(transaction); + // serialize the transaction + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForSigning(transaction); - // apply the signature on the transaction - transaction.signature = await secretKey.sign(serializedTransaction); + // apply the signature on the transaction + transaction.signature = await secretKey.sign(serializedTransaction); - console.log(transaction.toPlainObject()); - } + console.log(transaction.toPlainObject()); +} ``` #### Signing a Transaction by hash ```js - { - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); +{ + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const transaction = new Transaction({ - nonce: 90n, - sender: alice.address, - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - value: 1000000000000000000n, - gasLimit: 50000n, - chainID: "D", - }); + const transaction = new Transaction({ + nonce: 90n, + sender: alice.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D", + }); - const transactionComputer = new TransactionComputer(); + const transactionComputer = new TransactionComputer(); - // sets the least significant bit of the options field to `1` - transactionComputer.applyOptionsForHashSigning(transaction); + // sets the least significant bit of the options field to `1` + transactionComputer.applyOptionsForHashSigning(transaction); - // compute a keccak256 hash for signing - const hash = transactionComputer.computeHashForSigning(transaction); + // compute a keccak256 hash for signing + const hash = transactionComputer.computeHashForSigning(transaction); - // sign and apply the signature on the transaction - transaction.signature = await alice.signTransaction(transaction); + // sign and apply the signature on the transaction + transaction.signature = await alice.signTransaction(transaction); - console.log(transaction.toPlainObject()); - } + console.log(transaction.toPlainObject()); +} ``` #### Signing a Message using an Account: ```js - { - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); +{ + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); - const message = new Message({ - data: new Uint8Array(Buffer.from("hello")), - address: alice.address, - }); + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: alice.address, + }); - message.signature = await alice.signMessage(message); - } + message.signature = await alice.signMessage(message); +} ``` #### Signing a Message using an SecretKey: ```js - { - const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; - const secretKey = UserSecretKey.fromString(secretKeyHex); - const publicKey = secretKey.generatePublicKey(); +{ + const secretKeyHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; + const secretKey = UserSecretKey.fromString(secretKeyHex); + const publicKey = secretKey.generatePublicKey(); - const messageComputer = new MessageComputer(); - const message = new Message({ - data: new Uint8Array(Buffer.from("hello")), - address: publicKey.toAddress(), - }); - // serialized the message - const serialized = messageComputer.computeBytesForSigning(message); + const messageComputer = new MessageComputer(); + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: publicKey.toAddress(), + }); + // serialized the message + const serialized = messageComputer.computeBytesForSigning(message); - message.signature = await secretKey.sign(serialized); - } + message.signature = await secretKey.sign(serialized); +} ``` -md-start - ## Verifying signatures Signature verification is performed using an account’s public key. @@ -2974,97 +2944,97 @@ To simplify this process, we provide wrappers over public keys that make verific #### Verifying Transaction signature using a UserVerifier ```js - { - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const account = await Account.newFromPem(filePath); +{ + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); - const transaction = new Transaction({ - nonce: 90n, - sender: account.address, - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - value: 1000000000000000000n, - gasLimit: 50000n, - chainID: "D", - }); + const transaction = new Transaction({ + nonce: 90n, + sender: account.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D", + }); - // sign and apply the signature on the transaction - transaction.signature = await account.signTransaction(transaction); + // sign and apply the signature on the transaction + transaction.signature = await account.signTransaction(transaction); - // instantiating a user verifier; basically gets the public key - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const aliceVerifier = UserVerifier.fromAddress(alice); + // instantiating a user verifier; basically gets the public key + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const aliceVerifier = UserVerifier.fromAddress(alice); - // serialize the transaction for verification - const transactionComputer = new TransactionComputer(); - const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); + // serialize the transaction for verification + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); - // verify the signature - const isSignedByAlice = aliceVerifier.verify(serializedTransaction, transaction.signature); + // verify the signature + const isSignedByAlice = aliceVerifier.verify(serializedTransaction, transaction.signature); - console.log("Transaction is signed by Alice: ", isSignedByAlice); - } + console.log("Transaction is signed by Alice: ", isSignedByAlice); +} ``` #### Verifying Message signature using a UserVerifier ```ts - { - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const account = await Account.newFromPem(filePath); +{ + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); - const message = new Message({ - data: new Uint8Array(Buffer.from("hello")), - address: account.address, - }); + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: account.address, + }); - // sign and apply the signature on the message - message.signature = await account.signMessage(message); + // sign and apply the signature on the message + message.signature = await account.signMessage(message); - // instantiating a user verifier; basically gets the public key - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const aliceVerifier = UserVerifier.fromAddress(alice); + // instantiating a user verifier; basically gets the public key + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const aliceVerifier = UserVerifier.fromAddress(alice); - // serialize the message for verification - const messageComputer = new MessageComputer(); - const serializedMessage = messageComputer.computeBytesForVerifying(message); + // serialize the message for verification + const messageComputer = new MessageComputer(); + const serializedMessage = messageComputer.computeBytesForVerifying(message); - // verify the signature - const isSignedByAlice = await aliceVerifier.verify(serializedMessage, message.signature); + // verify the signature + const isSignedByAlice = await aliceVerifier.verify(serializedMessage, message.signature); - console.log("Message is signed by Alice: ", isSignedByAlice); - } + console.log("Message is signed by Alice: ", isSignedByAlice); +} ``` #### Verifying a signature using a public key ```js - { - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const account = await Account.newFromPem(filePath); +{ + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); - const transaction = new Transaction({ - nonce: 90n, - sender: account.address, - receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), - value: 1000000000000000000n, - gasLimit: 50000n, - chainID: "D", - }); + const transaction = new Transaction({ + nonce: 90n, + sender: account.address, + receiver: Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + value: 1000000000000000000n, + gasLimit: 50000n, + chainID: "D", + }); - // sign and apply the signature on the transaction - transaction.signature = await account.signTransaction(transaction); + // sign and apply the signature on the transaction + transaction.signature = await account.signTransaction(transaction); - // instantiating a public key - const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); - const publicKey = new UserPublicKey(alice.getPublicKey()); + // instantiating a public key + const alice = Address.newFromBech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"); + const publicKey = new UserPublicKey(alice.getPublicKey()); - // serialize the transaction for verification - const transactionComputer = new TransactionComputer(); - const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); + // serialize the transaction for verification + const transactionComputer = new TransactionComputer(); + const serializedTransaction = transactionComputer.computeBytesForVerifying(transaction); - // verify the signature - const isSignedByAlice = await publicKey.verify(serializedTransaction, transaction.signature); - console.log("Transaction is signed by Alice: ", isSignedByAlice); - } + // verify the signature + const isSignedByAlice = await publicKey.verify(serializedTransaction, transaction.signature); + console.log("Transaction is signed by Alice: ", isSignedByAlice); +} ``` #### Sending messages over boundaries @@ -3072,48 +3042,48 @@ Signed Message objects are typically sent to a remote party (e.g., a service), w To prepare a message for transmission, you can use the `MessageComputer.packMessage()` utility method. ```js - { - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const account = await Account.newFromPem(filePath); +{ + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const account = await Account.newFromPem(filePath); - const message = new Message({ - data: new Uint8Array(Buffer.from("hello")), - address: account.address, - }); + const message = new Message({ + data: new Uint8Array(Buffer.from("hello")), + address: account.address, + }); - // sign and apply the signature on the message - message.signature = await account.signMessage(message); + // sign and apply the signature on the message + message.signature = await account.signMessage(message); - const messageComputer = new MessageComputer(); - const packedMessage = messageComputer.packMessage(message); + const messageComputer = new MessageComputer(); + const packedMessage = messageComputer.packMessage(message); - console.log("Packed message", packedMessage); - } + console.log("Packed message", packedMessage); +} ``` Then, on the receiving side, you can use [`MessageComputer.unpackMessage()`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/MessageComputer.html#unpackMessage) to reconstruct the message, prior verification: ```js - { - const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); - const alice = await Account.newFromPem(filePath); - const messageComputer = new MessageComputer(); - const data = Buffer.from("test"); +{ + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + const messageComputer = new MessageComputer(); + const data = Buffer.from("test"); - const message = new Message({ - data: data, - address: alice.address, - }); + const message = new Message({ + data: data, + address: alice.address, + }); - message.signature = await alice.signMessage(message); - // restore message + message.signature = await alice.signMessage(message); + // restore message - const packedMessage = messageComputer.packMessage(message); - const unpackedMessage = messageComputer.unpackMessage(packedMessage); + const packedMessage = messageComputer.packMessage(message); + const unpackedMessage = messageComputer.unpackMessage(packedMessage); - // verify the signature - const isSignedByAlice = await alice.verifyMessageSignature(unpackedMessage, message.signature); + // verify the signature + const isSignedByAlice = await alice.verifyMessageSignature(unpackedMessage, message.signature); - console.log("Transaction is signed by Alice: ", isSignedByAlice); - } + console.log("Transaction is signed by Alice: ", isSignedByAlice); +} ``` diff --git a/cookbook/wallets.ts b/cookbook/wallets.ts index 4d402b2f..d8541d98 100644 --- a/cookbook/wallets.ts +++ b/cookbook/wallets.ts @@ -11,7 +11,6 @@ import { KeyPair, Mnemonic, UserPem, UserWallet } from "../src"; // md-ignore const mnemonic = Mnemonic.generate(); const words = mnemonic.getWords(); - console.log({ words }); // ``` // #### Saving the mnemonic to a keystore file @@ -25,7 +24,6 @@ import { KeyPair, Mnemonic, UserPem, UserWallet } from "../src"; // md-ignore const wallet = UserWallet.fromMnemonic({ mnemonic: mnemonic.toString(), password: "password" }); const filePath = path.join("../src", "testdata", "testwallets", "walletWithMnemonic.json"); - console.log({ filePath }); wallet.save(filePath); } // ``` diff --git a/src/wallet/userWallet.ts b/src/wallet/userWallet.ts index cc301723..a77d53a0 100644 --- a/src/wallet/userWallet.ts +++ b/src/wallet/userWallet.ts @@ -50,9 +50,7 @@ export class UserWallet { randomness = randomness || new Randomness(); const publicKey = secretKey.generatePublicKey(); - console.log({ secretKey, v: secretKey.valueOf() }); const data = Buffer.concat([secretKey.valueOf(), publicKey.valueOf()]); - console.log({ data }); const encryptedData = Encryptor.encrypt(data, password, randomness); return new UserWallet({ From 6e4e425b0933892429a9ae58796bd93538f5747f Mon Sep 17 00:00:00 2001 From: danielailie Date: Thu, 17 Apr 2025 15:12:57 +0300 Subject: [PATCH 10/15] Update generator --- cookbook/generate.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cookbook/generate.py b/cookbook/generate.py index 3188c555..6c16c318 100644 --- a/cookbook/generate.py +++ b/cookbook/generate.py @@ -26,6 +26,7 @@ DIRECTIVE_IGNORE = "// md-ignore" DIRECTIVE_UNINDENT = "// md-unindent" DIRECTIVE_AS_COMMENT = "// md-as-comment" +TO_UNINDENT_SPACE = " " TO_REMOVE = [ """(async () => {""", """})().catch((e) => { @@ -72,11 +73,17 @@ def render_file(input_file: Path) -> List[str]: if should_unindent: line = line.lstrip() + line = line.replace(DIRECTIVE_UNINDENT, "") + line = line.replace(DIRECTIVE_START, "") + line = line.replace(DIRECTIVE_AS_COMMENT, "") + if is_comment and not should_keep_as_comment: line = line.strip().strip("/").strip() + else: + if line.startswith(TO_UNINDENT_SPACE): + line = line[len(TO_UNINDENT_SPACE):] + - line = line.replace(DIRECTIVE_UNINDENT, "") - line = line.replace(DIRECTIVE_AS_COMMENT, "") line = line.rstrip() output_lines.append(line) From bdd0725187ab7499b1c804aea1a3facc411840bc Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 22 Apr 2025 17:00:36 +0300 Subject: [PATCH 11/15] Code review follow up --- cookbook/generate.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cookbook/generate.py b/cookbook/generate.py index 6c16c318..287d132e 100644 --- a/cookbook/generate.py +++ b/cookbook/generate.py @@ -27,6 +27,8 @@ DIRECTIVE_UNINDENT = "// md-unindent" DIRECTIVE_AS_COMMENT = "// md-as-comment" TO_UNINDENT_SPACE = " " + +# we don't want this ceremonial piece of code to show up in the rendered cookbook TO_REMOVE = [ """(async () => {""", """})().catch((e) => { @@ -54,10 +56,13 @@ def main(): def render_file(input_file: Path) -> List[str]: input_text = input_file.read_text() + for item in TO_REMOVE: input_text = input_text.replace(item, "") + input_lines = input_text.splitlines() start = input_lines.index(DIRECTIVE_START) + input_lines = input_lines[start:] output_lines: List[str] = [] From fabd74f2c901d025fb9a9179d103f025625f9b45 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 29 Apr 2025 11:01:35 +0300 Subject: [PATCH 12/15] Fix typo --- cookbook/cookbook.md | 8 ++++---- cookbook/generate.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cookbook/cookbook.md b/cookbook/cookbook.md index ada24e4c..5ffac814 100644 --- a/cookbook/cookbook.md +++ b/cookbook/cookbook.md @@ -919,10 +919,10 @@ This allows arguments to be passed as native Javascript values. If the ABI is no ``` :::tip -When creating transactions using [`SmartContractController`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/SmartContractController.html) or [`SmartContractTransactionsFactory`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/SmartContractTransactionsFactory.html), even if the ABI is available and provided, -you can still use [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/TypedValue.html) objects as arguments for deployments and interactions. +When creating transactions using [`SmartContractController`](https://multiversx.github.io/mx-sdk-js-core/v14/classes/SmartContractController.html) or [`SmartContractTransactionsFactory`](https://multiversx.github.io/mx-sdk-js-core/v14/classes/SmartContractTransactionsFactory.html), even if the ABI is available and provided, +you can still use [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TypedValue.html) objects as arguments for deployments and interactions. -Even further, you can use a mix of [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/TypedValue.html) objects and plain JavaScript values and objects. For example: +Even further, you can use a mix of [`TypedValue`](https://multiversx.github.io/mx-sdk-js-core/v14/classes/TypedValue.html) objects and plain JavaScript values and objects. For example: ```js let args = [new U32Value(42), "hello", { foo: "bar" }, new TokenIdentifierValue("TEST-abcdef")]; @@ -3061,7 +3061,7 @@ To prepare a message for transmission, you can use the `MessageComputer.packMess } ``` -Then, on the receiving side, you can use [`MessageComputer.unpackMessage()`](https://multiversx.github.io/mx-sdk-js-core/v13/classes/MessageComputer.html#unpackMessage) to reconstruct the message, prior verification: +Then, on the receiving side, you can use [`MessageComputer.unpackMessage()`](https://multiversx.github.io/mx-sdk-js-core/v14/classes/MessageComputer.html#unpackMessage) to reconstruct the message, prior verification: ```js { diff --git a/cookbook/generate.py b/cookbook/generate.py index 287d132e..0f2dc155 100644 --- a/cookbook/generate.py +++ b/cookbook/generate.py @@ -36,7 +36,7 @@ });"""] API_URL = "https://multiversx.github.io/mx-sdk-js-core" -API_DEFAIULT_VERSION = "v13" +API_DEFAIULT_VERSION = "v14" DOCS_URL = "https://docs.multiversx.com" def main(): From 722611f238de4b4a06df45ee464d747c78852cda Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 29 Apr 2025 11:06:25 +0300 Subject: [PATCH 13/15] Fix typo --- cookbook/generate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cookbook/generate.py b/cookbook/generate.py index 0f2dc155..e7a78fa5 100644 --- a/cookbook/generate.py +++ b/cookbook/generate.py @@ -36,7 +36,7 @@ });"""] API_URL = "https://multiversx.github.io/mx-sdk-js-core" -API_DEFAIULT_VERSION = "v14" +API_DEFAULT_VERSION = "v14" DOCS_URL = "https://docs.multiversx.com" def main(): @@ -102,10 +102,10 @@ def render_api_links(input: str) -> str: for match in matches_func: [class_name, method] = match.split(".") - input = input.replace(f"`func:{match}()`", f"[`{match}()`]({API_URL}/{API_DEFAIULT_VERSION}/classes/{class_name}.html#{method})") + input = input.replace(f"`func:{match}()`", f"[`{match}()`]({API_URL}/{API_DEFAULT_VERSION}/classes/{class_name}.html#{method})") for match in matches_class: - input = input.replace(f"`class:{match}`", f"[`{match}`]({API_URL}/{API_DEFAIULT_VERSION}/classes/{match}.html)") + input = input.replace(f"`class:{match}`", f"[`{match}`]({API_URL}/{API_DEFAULT_VERSION}/classes/{match}.html)") return input From e6958ab0c00ef6bed864366227514febe2cf6701 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 29 Apr 2025 13:03:21 +0300 Subject: [PATCH 14/15] Increase sleep for localnet --- .github/workflows/test-localnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-localnet.yml b/.github/workflows/test-localnet.yml index d324b54d..2f1800a0 100644 --- a/.github/workflows/test-localnet.yml +++ b/.github/workflows/test-localnet.yml @@ -42,7 +42,7 @@ jobs: mkdir -p ~/localnet && cd ~/localnet mxpy localnet setup --configfile=${GITHUB_WORKSPACE}/localnet.toml nohup mxpy localnet start --configfile=${GITHUB_WORKSPACE}/localnet.toml > localnet.log 2>&1 & echo $! > localnet.pid - sleep 10 # Allow time for the testnet to fully start + sleep 30 # Allow time for the testnet to fully start # Step 6: Install Node.js and dependencies - name: Set up Node.js environment From 6f3cb3960693a8ef2002fa359a2affcb68ac9322 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 29 Apr 2025 15:06:57 +0300 Subject: [PATCH 15/15] Increase sleep for localnet --- .github/workflows/test-localnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-localnet.yml b/.github/workflows/test-localnet.yml index 2f1800a0..61cdae4c 100644 --- a/.github/workflows/test-localnet.yml +++ b/.github/workflows/test-localnet.yml @@ -42,7 +42,7 @@ jobs: mkdir -p ~/localnet && cd ~/localnet mxpy localnet setup --configfile=${GITHUB_WORKSPACE}/localnet.toml nohup mxpy localnet start --configfile=${GITHUB_WORKSPACE}/localnet.toml > localnet.log 2>&1 & echo $! > localnet.pid - sleep 30 # Allow time for the testnet to fully start + sleep 120 # Allow time for the testnet to fully start # Step 6: Install Node.js and dependencies - name: Set up Node.js environment