diff --git a/docs/api/_category_.json b/docs/api/_category_.json index eeedcec..e644ddf 100644 --- a/docs/api/_category_.json +++ b/docs/api/_category_.json @@ -1,4 +1,4 @@ { "label": "Gear-JS", - "position": 9 + "position": 10 } diff --git a/docs/api/api.mdx b/docs/api/api.mdx index aa1d769..2789b0b 100644 --- a/docs/api/api.mdx +++ b/docs/api/api.mdx @@ -7,7 +7,7 @@ sidebar_label: Getting Started ## Gear-JS API -The Gear-JS API offers a set of utilities, libraries, and tools that enable JavaScript applications to interact with programs running on the Vara network via queries to a Vara node. +The Gear-JS API offers a set of utilities, libraries, and tools that enable JavaScript applications to interact with programs running on the Vara network via queries to a Vara node. For applications built using the [Sails](/docs/build/sails) framework, it is generally recommended to use [Sails-JS](/docs/sails-js), which leverages the Gear-JS API for low-level communication with Vara nodes. The sections below describe tools for implementing basic functions in a JS application, such as managing key pairs (accounts), calculating gas required for network operations, uploading programs to the network, sending messages to programs, reading program states, retrieving messages from the user's mailbox, working with metadata, and more. Useful code snippets are provided in the Cookbook section. diff --git a/docs/build/sails/coreconcepts.md b/docs/build/sails/coreconcepts.md index adaf69e..b439134 100644 --- a/docs/build/sails/coreconcepts.md +++ b/docs/build/sails/coreconcepts.md @@ -66,7 +66,7 @@ impl MyService { The program's main responsibility is to host one or more services and expose them to external consumers. The structure of a program is somewhat simpler compared to that of services. As with services, there are two possible variants for its **public** methods: -- **Application Constructors**: Methods working over `Self` are treated as application constructors. These functions can accept some parameters +- **Application Constructors**: Methods returning `Self` are treated as application constructors. These functions can accept some parameters passed by a client and can be synchronous or asynchronous. One of them will be called once at the very beginning of the application lifetime, i.e. when the application is loaded onto the network. The returned program instance will live until the application diff --git a/docs/gcli/_category_.json b/docs/gcli/_category_.json index 983ed31..b03294d 100644 --- a/docs/gcli/_category_.json +++ b/docs/gcli/_category_.json @@ -1,5 +1,5 @@ { "label": "Gear-CLI Tools", - "position": 10 + "position": 11 } \ No newline at end of file diff --git a/docs/general/_category_.json b/docs/general/_category_.json index 85f0722..94c85fe 100644 --- a/docs/general/_category_.json +++ b/docs/general/_category_.json @@ -1,4 +1,4 @@ { "label": "General topics", - "position": 14 + "position": 15 } diff --git a/docs/governance/_category_.json b/docs/governance/_category_.json index 703618e..cd7e932 100644 --- a/docs/governance/_category_.json +++ b/docs/governance/_category_.json @@ -1,4 +1,4 @@ { "label": "Governance", - "position": 13 + "position": 14 } diff --git a/docs/sails-js/_category_.json b/docs/sails-js/_category_.json new file mode 100644 index 0000000..43810e5 --- /dev/null +++ b/docs/sails-js/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Sails-JS", + "position": 9 +} diff --git a/docs/sails-js/client-generation.md b/docs/sails-js/client-generation.md new file mode 100644 index 0000000..53f848f --- /dev/null +++ b/docs/sails-js/client-generation.md @@ -0,0 +1,276 @@ +--- +sidebar_position: 3 +sidebar_label: Client Generation +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Client Generation + +The `sails-js-cli` is a command-line tool designed to generate TypeScript client libraries from Sails IDL files. It automates the creation of fully functional client libraries based on the interfaces defined in the Sails framework, streamlining development and ensuring consistency between client and on-chain programs. + +## Installation + +To install the `sails-js-cli` package globally, run the following command: + +```bash +npm install -g sails-js-cli +``` + +Alternatively, you can use `npx` to run the command without installing the package: + +```bash +npx sails-js-cli command ...args +``` + +## Generating a TypeScript Client Library Using an IDL File + +To generate a TypeScript client library, run the following command: + +```bash +sails-js generate path/to/sails.idl -o path/to/out/dir +``` + +If you want to avoid global installation, use `npx`: + +```bash +npx sails-js-cli generate path/to/sails.idl -o path/to/out/dir +``` + +To generate only the `lib.ts` file without the full project structure, use the `--no-project` flag: + +```bash +sails-js generate path/to/sails.idl -o path/to/out/dir --no-project +``` + + + +## Using the Generated Library + +### Creating an Instance + +First, connect to the chain using `@gear-js/api`: + +```javascript +import { GearApi } from '@gear-js/api'; + +const api = await GearApi.create(); +``` + +For further details, refer to the [Gear-JS](/docs/api) API section. Next, import the `Program` class from the generated file and create an instance: + +```javascript +import { Program } from './lib'; + +const program = new Program(api); + +// If the program is already deployed, provide its ID +const programId = '0x...'; +const program = new Program(api, programId); +``` + +The `Program` class includes all the functions defined in the IDL file. + +## Methods of the `Program` Class + +The `Program` class contains several types of methods: + +- Query methods +- Message methods +- Constructor methods +- Event subscription methods + +### Query Methods + +Query methods are used to query the program's state. These methods accept the required arguments for the function call and return the result. Additionally, they accept optional parameters: `originAddress` (the caller's account address, defaulting to a zero address if not provided), `value` (an optional amount of tokens for function execution), and `atBlock` (to query the program state at a specific block). + +```javascript +const alice = '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'; +const result = await program.serviceName.queryFnName(arg1, arg2, alice); +console.log(result); +``` + +### Message Methods + +Message methods are used to send messages to the program. These methods accept the required arguments for sending the message and return a [Transaction Builder](transaction-builder.md), which contains methods for building and sending the transaction. + +```javascript +const transaction = program.serviceName.functionName(arg1, arg2); + +// ## Set the account sending the message + +// Using a KeyringPair instance +import { Keyring } from '@polkadot/api'; +const keyring = new Keyring({ type: 'sr25519' }); +const pair = keyring.addFromUri('//Alice'); +transaction.withAccount(pair); + +// Or using an address and signer options (common in frontend applications with connected wallets) +import { web3FromSource, web3Accounts } from '@polkadot/extension-dapp'; +const allAccounts = await web3Accounts(); +const account = allAccounts[0]; +const injector = await web3FromSource(account.meta.source); +transaction.withAccount(account.address, { signer: injector.signer }); + +// ## Set the value of the message +transaction.withValue(BigInt(10 * 1e12)); // 10 VARA + +// ## Calculate gas +// Optionally, you can provide two arguments: +// The first argument, `allowOtherPanics`, either allows or forbids panics in other programs (default: false). +// The second argument, `increaseGas`, is the percentage to increase the gas limit (default: 0). +await transaction.calculateGas(); + +// Alternatively, use `withGas` to set the gas limit manually: +transaction.withGas(100000n); + +// ## Send the transaction +// `signAndSend` returns the message ID, block hash, and a `response` function for retrieving the program's response. +const { msgId, blockHash, response } = await transaction.signAndSend(); +const result = await response(); +console.log(result); +``` + +### Constructor Methods + +Constructor methods, postfixed with `CtorFromCode` and `CtorFromCodeId` in the `Program` class, are used to deploy the program on the chain. These methods accept either the Wasm code bytes or the code ID and return a [Transaction Builder](transaction-builder.md) just like message methods. + +```javascript +const code = fs.readFileSync('path/to/program.wasm'); +// Or use the fetch function to retrieve the code in frontend environments +const transaction = program.newCtorFromCode(code); + +// The same methods as message methods can be used to build and send the transaction. +``` + +### Event Subscription Methods + +Event subscription methods allow subscribing to specific events emitted by the program. + +```javascript +program.subscribeToSomeEvent((data) => { + console.log(data); +}); +``` + + + +## Example: The Demo Project + +The following example demonstrates how the generated `lib.ts` can be used in conjunction with `@gear-js/api` to upload a WASM binary of an application to the chain, create and submit a service command to the deployed application, and receive its response. This example is part of the Sails GitHub repository and can be viewed [here](https://github.com/gear-tech/sails/tree/master/js/example). Although the project includes multiple services, this example focuses on interacting with the `pingPong` service. The relevant portion of the IDL looks like this: + +```rust +service PingPong { + Ping : (input: str) -> result (str, str); +}; +``` + +The script first initializes the `GearApi` and creates a keyring to retrieve Alice’s account using the `@polkadot/api` library, which is used for signing transactions. It then reads the compiled WASM application and deploys it to the network using `newCtorFromCode` from the generated `lib.ts`, which creates a `TransactionBuilder` for constructing the deployment transaction. After deployment, the script interacts with the program by invoking the `ping` function from the `pingPong` service, constructing and sending a new transaction. Finally, the program's reply is awaited and logged to the console. + +```jsx +import { GearApi } from '@gear-js/api'; +import { Keyring } from '@polkadot/api'; +import { Program } from './lib.js'; +import { readFileSync } from 'fs'; + +const main = async () => { + const api = await GearApi.create(); + const keyring = new Keyring({ type: 'sr25519', ss58Format: 137 }); + + const alice = keyring.addFromUri('//Alice'); + + const program = new Program(api); + + // Deploy the program + + const code = readFileSync('../../target/wasm32-unknown-unknown/release/demo.opt.wasm'); + + const ctorBuilder = await program.newCtorFromCode(code, null, null).withAccount(alice).calculateGas(); + const { blockHash, msgId, txHash } = await ctorBuilder.signAndSend(); + + console.log( + `\nProgram deployed. \n\tprogram id ${program.programId}, \n\tblock hash: ${blockHash}, \n\ttx hash: ${txHash}, \n\tinit message id: ${msgId}`, + ); + + // Call the program + + const pingBuilder = await program.pingPong.ping('ping').withAccount(alice).calculateGas(); + const { blockHash: blockHashPing, msgId: msgIdPing, txHash: txHashPing, response } = await pingBuilder.signAndSend(); + + console.log( + `\nPing message sent. \n\tBlock hash: ${blockHashPing}, \n\ttx hash: ${txHashPing}, \n\tmessage id: ${msgIdPing}`, + ); + const reply = await response(); + console.log(`\nProgram replied: \n\t${JSON.stringify(reply)}`); +}; + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.log(error); + process.exit(1); + }); +``` \ No newline at end of file diff --git a/docs/sails-js/overview.md b/docs/sails-js/overview.md new file mode 100644 index 0000000..35b66b1 --- /dev/null +++ b/docs/sails-js/overview.md @@ -0,0 +1,214 @@ +--- +sidebar_position: 1 +sidebar_label: Library Overview +--- + +# Library Overview + +The `sails-js` library workflow begins by creating a `Sails` object through parsing a provided IDL description. This process maps the interface defined by the IDL to corresponding objects in the `Sails` instance, including: + +- Constructors +- Services +- Functions (referred to as Commands in the Sails framework) +- Queries +- Events + +The library also offers methods for decoding information from payloads and encoding data to be sent to a program via transactions. The `TransactionBuilder` class facilitates the building and sending of these transactions to the blockchain. For further details, refer to the [Transaction Builder](transaction-builder.md) section. + +## Parsing an IDL Description + +```javascript +import { Sails } from 'sails-js'; +import { SailsIdlParser } from 'sails-js-parser'; + +const parser = await SailsIdlParser.new(); +const sails = new Sails(parser); + +const idl = ''; + +sails.parseIdl(idl); +``` + +The `sails` object now contains all the constructors, services, functions, and events available in the IDL file. + +To send messages, create programs, and subscribe to events using `Sails`, you need to connect to the chain using `@gear-js/api` and set the `GearApi` instance using the `setApi` method. For further details, refer to the [Gear-JS](/docs/api) API section. + + +```javascript +import { GearApi } from '@gear-js/api'; + +const api = await GearApi.create(); + +sails.setApi(api); +``` +## The `Sails` Class +### Constructors + +The `sails.ctors` property contains an object with all the constructors available in the IDL file. The keys are the constructor names, and each value is an object with the following properties: + +```javascript +{ + args: Array<{ name: string, type: string }>, // Array of arguments with their names and SCALE codec types + encodePayload: (...args: any) => HexString, // Function to encode the payload + decodePayload: (bytes: HexString) => any, // Function to decode the payload + fromCode: ( + code: Uint8Array | Buffer, + ...args: unknown[] + ) => TransactionBuilder, // Function to create a transaction builder to deploy the program using code bytes + fromCodeId: ( + codeId: string, + ...args: unknown[] + ) => TransactionBuilder // Function to create a transaction builder to deploy the program using a code ID +} +``` + +To get a constructor object, use `sails.ctors.ConstructorName`. + +The `fromCode` and `fromCodeId` methods return an instance of the `TransactionBuilder` class, which can be used to build and send the transaction to the chain. + +### Services + +The `sails.services` property contains an object with all the services available in the IDL file. The keys are the service names, and each value is an object with the following properties: + +```javascript +{ + functions: Record, // Object with all the functions available in the service + queries: Record, // Object with all the queries available in the service + events: Record, // Object with all the events available in the service +} +``` + +To get a service object, use `sails.services.ServiceName`. + +### Functions + +The `sails.services.ServiceName.functions` property contains an object with all the functions from the IDL file that can be used to send messages to the program. The keys are the function names, and each value can be used either as a function that accepts arguments and returns an instance of the `TransactionBuilder` class, or as an object with the following properties: + +```javascript +{ + args: Array<{ name: string, type: string }>, // Array of arguments with their names and SCALE codec types + returnType: any, // SCALE codec definition of the return type + encodePayload: (...args: any) => Uint8Array, // Function to encode the payload + decodePayload: (bytes: Uint8Array) => any, // Function to decode the payload + decodeResult: (result: Uint8Array) => any // Function to decode the result +} +``` + +It's necessary to provide a program ID so that the function can be called. You can set the program ID using the `.setProgramId` method of the `Sails` class: + +```javascript +sails.setProgramId('0x...'); +``` + +To create a transaction for a function call, you can do the following: + +```javascript +const transaction = sails.services.ServiceName.functions.FunctionName(arg1, arg2); +``` + +### Queries + +The `sails.services.ServiceName.queries` property contains an object with all the queries from the IDL file that can be used to read the program state. The keys are the query names. The values include the same properties as described in the [Functions](#functions) section above. Note that the query function returns the result of the query, not a transaction builder. + +*The query function accepts three additional arguments beyond those specified in the IDL:* + +- **`originAddress`**: The address of the account that is calling the function. +- **`value`**: *(Optional, default is `0`)* The amount of tokens sent with the function call. +- **`atBlock`**: *(Optional)* The block at which the query is executed. + +**Example:** + +```javascript +const alice = 'kGkLEU3e3XXkJp2WK4eNpVmSab5xUNL9QtmLPh8QfCL2EgotW'; +// functionArg1, functionArg2 are the arguments of the query function from the IDL file +const result = await sails.services.ServiceName.queries.QueryName( + alice, + null, + null, + functionArg1, + functionArg2 +); + +console.log(result); +``` + +In this example, `alice` is the origin address, and `functionArg1`, `functionArg2` are the arguments specific to the query function as defined in your IDL file. The `null` values indicate that the optional `value` and `atBlock` parameters are not being specified. + +### Events + +The `sails.services.ServiceName.events` property contains an object with all the events available in the IDL file. The keys are the event names, and each value is an object with the following properties: + +```javascript +{ + type: any, // SCALE codec definition of the event + is: (event: UserMessageSent) => bool, // Function to check if the event is of the specific type + decode: (data: Uint8Array) => any, // Function to decode the event data + subscribe: (callback: (data: any) => void | Promise) => void // Function to subscribe to the event +} +``` + +To subscribe to an event, use the `subscribe` method of the event object: + +```javascript +sails.services.ServiceName.events.EventName.subscribe((data) => { + console.log(data); +}); +``` + +This will call the provided callback function whenever the event occurs, with `data` containing the decoded event data. + +### Get Function Name and Decode Bytes + +You can extract service and function names from payload bytes and decode messages using the following methods: + +- Use the `getServiceNamePrefix` function to get the service name from the payload bytes. +- Use the `getFnNamePrefix` method to get the function or event name from the payload bytes. +- Use the `sails.services.ServiceName.functions.FunctionName.decodePayload` method to decode the payload bytes of the sent message. +- Use the `sails.services.ServiceName.functions.FunctionName.decodeResult` method to decode the result bytes of the received message. + +**Example:** + +```javascript +import { getServiceNamePrefix, getFnNamePrefix } from 'sails-js'; + +const payloadOfSentMessage = '0x'; +const serviceName = getServiceNamePrefix(payloadOfSentMessage); +const functionName = getFnNamePrefix(payloadOfSentMessage); + +console.log( + sails.services[serviceName].functions[functionName].decodeResult(payloadOfSentMessage) +); + +const payloadOfReceivedMessage = '0x'; + +console.log( + sails.services[serviceName].functions[functionName].decodePayload(payloadOfReceivedMessage) +); +``` + +**Note:** Ensure that you use `sails.services[serviceName]` when accessing services. + +### Encode and Decode Constructors and Events + +You can use the same approach to encode and decode bytes of constructors or events: + +```javascript +// Encoding and decoding constructor payloads +sails.ctors.ConstructorName.encodePayload(arg1, arg2); +sails.ctors.ConstructorName.decodePayload(''); + +// Decoding event data +sails.events.EventName.decode(''); +``` + +### Encode Payload + +Use the `sails.services.ServiceName.functions.FunctionName.encodePayload` method to encode the payload for a specific function. The bytes returned by this method can be used to send a message to the program. + +**Example:** + +```javascript +const payload = sails.services.ServiceName.functions.FunctionName.encodePayload(arg1, arg2); +``` + +In this example, `arg1` and `arg2` are the arguments required by the function as defined in your IDL file. The encoded `payload` can then be sent in a transaction to the program. \ No newline at end of file diff --git a/docs/sails-js/react-hooks.md b/docs/sails-js/react-hooks.md new file mode 100644 index 0000000..7f910de --- /dev/null +++ b/docs/sails-js/react-hooks.md @@ -0,0 +1,270 @@ +--- +sidebar_position: 4 +sidebar_label: React Hooks +--- + +# React Hooks + +# React Hooks + +The [`@gear-js/react-hooks`](/docs/api/tooling/react-hooks.md) library provides React hook abstractions over `sails-js` and its [generated program library](client-generation.md). This significantly simplifies the development of front-end applications built with Sails. + +[TanStack Query](https://tanstack.com/query) is used as the async state manager to handle queries and mutations, making it easier to manage asynchronous operations like fetching data (queries) and performing create/update/delete operations (mutations) against the defined program library. Therefore, most hooks' parameters and return values correspond to the library's conventions. + +## Hooks + +### `useSails` + +The `useSails` hook returns a `Sails` instance as described in the [Library Overview](overview.md#the-sails-class) section, based on the provided `programId` and IDL description of the deployed application. + +**Example:** + +```js +import { useSails } from '@gear-js/react-hooks'; + +const programId = '0x...'; +const idl = '...'; + +const { data } = useSails({ + programId, + idl, +}); + +console.log(data); +``` + +### `useProgram` + +The `useProgram` hook returns a `Program` instance as described in the [Client Generation](client-generation.md#methods-of-the-program-class) section using a generated library (`lib.ts`) and the `id` of a deployed application. + +**Example:** + +```js +import { useProgram } from '@gear-js/react-hooks'; +import { Program } from './lib'; + +const { data } = useProgram({ + library: Program, + id: '0x...', +}); + +console.log(data); +``` + +### `useSendProgramTransaction` + +The `useSendProgramTransaction` hook is used to call a `function` (also referred to as a [command](/docs/build/sails/coreconcepts.md#service) in Sails) of a Sails `service` from the application deployed at `id`. It returns a mutation to sign and send a transaction for this purpose. + +> **Note:** The returned mutation callback is essentially a wrapper over the [`TransactionBuilder`](transaction-builder.md) in `sails-js` and its `signAndSend` method, which will be called upon mutation execution. The `useSendProgramTransaction` hook abstracts this process by internally specifying an account and a [`@gear-js/api`](/docs/api/api.mdx) instance, making it a shortcut for this procedure. + +**Example:** + +```jsx +import { useProgram, useSendProgramTransaction } from '@gear-js/react-hooks'; +import { Program } from './lib'; + +function SendTransaction() { + const { data: program } = useProgram({ + library: Program, + id: '0x...', + }); + + const { sendTransactionAsync } = useSendProgramTransaction({ + program, + serviceName: 'service', + functionName: 'function', + }); + + const handleClick = async () => { + const result = await sendTransactionAsync({ + args: ['arg', 'anotherArg'], + account: { addressOrPair: '0x...' }, // Defaults to the connected account from the extension if not provided + value: 1000000n, // Defaults to 0 if not provided + gasLimit: 1000000000n, // Automatically calculated if not provided + voucherId: '0x...', // If not provided, the transaction will be sent without a voucher + }); + + const response = await result.response; + console.log('response: ', response); + }; + + return ( + + ); +} + +export { SendTransaction }; +``` + +### `useProgramQuery` + +The `useProgramQuery` hook is used to call a `query` of a Sails `service` from the application deployed at `id`. It returns a query containing the program's current state. + +**Example:** + +```jsx +import { useProgram, useProgramQuery } from '@gear-js/react-hooks'; +import { Program } from './lib'; + +function State() { + const { data: program } = useProgram({ + library: Program, + id: '0x...', + }); + + const { data } = useProgramQuery({ + program, + serviceName: 'service', + functionName: 'query', + args: ['arg', 'anotherArg'], + watch: false, // If true, initializes a subscription to the program's state changes in the Gear MessagesDispatched event. Can increase network traffic. + }); + + return
{JSON.stringify(data)}
; +} +``` + +### `useProgramEvent` + +The `useProgramEvent` hook initializes a subscription to a specified program event. + +**Example:** + +The `useProgramEvent` hook below sets up a listener on the `event` function of the specified `service` and executes the `onData` callback with the new data whenever the event is triggered. + +```jsx +import { useProgram, useProgramEvent } from '@gear-js/react-hooks'; +import { Routing } from './pages'; +import { Program } from './lib'; + +function App() { + const { data: program } = useProgram({ + library: Program, + id: '0x...', + }); + + useProgramEvent({ + program, + serviceName: 'service', + functionName: 'event', + onData: (data) => console.log(data), + }); + + return ( +
+ +
+ ); +} + +export { App }; +``` + +## Prepare Hooks + +When performing long-running asynchronous work in response to a user transaction using [`useSendProgramTransaction`](#usesendprogramtransaction)) hook, the user only receives feedback on the transaction status after transaction is complete. Prepare hooks can be used to eagerly obtain values before user interaction or to perform upfront validation before sending a transaction. + +### `usePrepareProgramTransaction` + +The `usePrepareProgramTransaction` hook returns a mutation that retrieves the intermediate transaction state for a specified `function` and `service` of a deployed application at `id`. + +This hook is useful for eagerly obtaining values such as `gasLimit`, `extrinsic`, and `transactionFee`, which are essential for providing a smoother user experience. + +**Example:** + +The example below shows how to obtain the `transactionFee` for a transaction, which can be used to validate or display the fee to the user before proceeding. + +```jsx +import { useProgram, usePrepareProgramTransaction } from '@gear-js/react-hooks'; +import { Program } from './lib'; + +function LogTransactionFeeButton() { + const { data: program } = useProgram({ + library: Program, + id: '0x...', + }); + + const { data, prepareTransactionAsync } = usePrepareProgramTransaction({ + program, + serviceName: 'service', + functionName: 'function', + }); + + const handleClick = async () => { + const transaction = await prepareTransactionAsync({ + args: ['arg', 'anotherArg'], + account: { addressOrPair: '0x...' }, + value: 1000000n, + gasLimit: 1000000000n, + voucherId: '0x...', + }); + + const fee = await transaction.transactionFee(); + console.log('fee: ', fee); + }; + + return ( + + ); +} + +export { LogTransactionFeeButton }; +``` + +**Example:** + +Using the hook in combination with [useSendProgramTransaction](#usesendprogramtransaction): + +```jsx +import { useProgram, usePrepareProgramTransaction, useSendProgramTransaction } from '@gear-js/react-hooks'; +import { Program } from './lib'; + +function SendPreparedTransaction() { + const { data: program } = useProgram({ + library: Program, + id: '0x...', + }); + + const { prepareTransactionAsync } = usePrepareProgramTransaction({ + program, + serviceName: 'service', + functionName: 'function', + }); + + const { sendTransactionAsync } = useSendProgramTransaction({ + program, + serviceName: 'service', + functionName: 'function', + }); + + const handleClick = async () => { + const transaction = await prepareTransactionAsync({ + args: ['arg', 'anotherArg'], + account: { addressOrPair: '0x...' }, + value: 1000000n, + gasLimit: 1000000000n, + voucherId: '0x...', + }); + + const fee = await transaction.transactionFee(); + console.log('fee: ', fee); + + const result = await sendTransactionAsync(transaction); + const response = await result.response; + + console.log('response: ', response); + }; + + return ( + + ); +} + +export { SendPreparedTransaction }; +``` \ No newline at end of file diff --git a/docs/sails-js/sails-js.md b/docs/sails-js/sails-js.md new file mode 100644 index 0000000..544814c --- /dev/null +++ b/docs/sails-js/sails-js.md @@ -0,0 +1,54 @@ +--- +sidebar_position: 1 +sidebar_label: Sails-JS +--- + +# Sails-JS + +The Sails-JS library is a powerful tool designed to help developers interact with their [Sails](/docs/build/sails/) applications on Vara Network. It facilitates seamless communication between your TypeScript projects and on-chain programs by providing two primary methods for generating client libraries from a Sails IDL (Interface Definition Language), while relying on the [Gear-JS API](/docs/api) library for low-level communication with Vara nodes and network interactions: + +1. **Parsing IDL with the Sails Class** + + Use the `.parseIdl` method of the `Sails` class to parse a string containing the IDL description of your Sails application during runtime. This method instantiates a corresponding `Sails` object, which you can use to interact directly with your deployed application. This approach offers maximal control over the interaction with your application. + + *Why Choose This Method?* + + - **Fine-Grained Control**: Developers may want to manage low-level operations or handle side effects of the library's abstractions themselves. Using the raw `Sails` object gives you the flexibility to customize interactions as needed. Parsing the IDL at runtime can be also be advantageous if you need to work with multiple IDL files. + +2. **Generating Client Libraries with `sails-js-cli`** + + The `sails-js-cli` command-line tool automates the generation of TypeScript client libraries from a given IDL file. The tool produces a `lib.ts` file that you can import into your TypeScript project. This generated library provides strongly typed classes and methods corresponding to your application's services, functions, queries, and events, making it easier and faster to interact with your deployed application. + + *Why Choose This Method?* + + - **Ease of Use**: The auto-generated client library simplifies the development process, allowing you to get started quickly without worrying about low-level details. + - **Improved Developer Experience**: By providing abstractions over complex operations, it enables you to focus on building features rather than managing underlying mechanisms. + + +## Installation + +The `sails-js` library requires the `@gear-js/api` and `@polkadot/api` packages to be installed. To install `sails-js` using npm, run the following command: + +```sh +npm install sails-js +``` + +or using yarn: + +```sh +yarn add sails-js +``` + +## Getting Started + +To begin using `sails-js`, choose the method that best fits your development workflow: + +- **Maximum Control with Dynamic Interaction** + + If you need fine-grained control over low-level operations or wish to handle side effects yourself, consider using the `.parseIdl` method to work with the raw `Sails` object. This approach is suitable for developers who require customization beyond what the generated client library offers. Refer to the [Overview](overview.md) section, which explains how to use the `Sails` class and its methods. + +- **Quick Start with Generated Client Library** + + If you prefer a faster setup and a straightforward way to interact with your application, the generated client library is an excellent starting point. It abstracts away many complexities, making it ideal for developers who want to focus on application logic. See the [Client Generation](client-generation.md) section for guidance on using the `sails-js-cli` tool and integrating the generated `lib.ts` into your project. + +**The Sails-JS code is available on [GitHub](https://github.com/gear-tech/sails).** diff --git a/docs/sails-js/transaction-builder.md b/docs/sails-js/transaction-builder.md new file mode 100644 index 0000000..7e8edd6 --- /dev/null +++ b/docs/sails-js/transaction-builder.md @@ -0,0 +1,134 @@ +--- +sidebar_position: 2 +sidebar_label: Transaction Builder +--- + +# Transaction Builder + +The `TransactionBuilder` class is used to build and send transactions to the chain. + +Use the `.programId` property to get the ID of the program that is used in the transaction. + +## Methods to Build and Send the Transaction +### `withAccount` + +The `withAccount` method sets the account that is sending the message. It accepts either a `KeyringPair` instance or an address with `signerOptions`. + +**Example using a `KeyringPair` instance:** + +```jsx +import { Keyring } from '@polkadot/api'; +const keyring = new Keyring({ type: 'sr25519' }); +const pair = keyring.addFromUri('//Alice'); + +// Set the account using the KeyringPair instance +transaction.withAccount(pair); +``` + +**Example using an address and signer options (commonly used on the frontend with a connected wallet):** + +```jsx +// This case is mostly used on the frontend with a connected wallet. +import { web3FromSource, web3Accounts } from '@polkadot/extension-dapp'; + +// Retrieve all accounts from the wallet extension +const allAccounts = await web3Accounts(); +const account = allAccounts[0]; + +// Get the injector for the account's source +const injector = await web3FromSource(account.meta.source); + +// Set the account address and signer in the transaction +transaction.withAccount(account.address, { signer: injector.signer }); +``` + +### `withValue` + +The `withValue` method sets the value (amount) to be sent with the message. + +**Example:** + +```jsx +// Set the value to 10 VARA +transaction.withValue(BigInt(10 * 1e12)); +``` + +### `calculateGas` +The `calculateGas` method calculates the gas limit of the transaction. Optionally, you can provide two arguments: +- The first argument, `allowOtherPanics`, determines whether panics in other programs are allowed to be triggered. It is set to `false` by default. +- The second argument, `increaseGas`, is the percentage to increase the gas limit. It is set to `0` by default. + +**Example:** + +```jsx +// Calculate gas limit with default options +await transaction.calculateGas(); + +// Calculate gas limit allowing other panics and increasing gas limit by 10% +await transaction.calculateGas(true, 10); +``` + +### `withGas` + +The `withGas` method manually sets the gas limit of the transaction. This can be used instead of the `calculateGas` method. + +**Example:** + +```jsx +// Set the gas limit manually +transaction.withGas(100_000_000_000n); +``` + +### `withVoucher` + +The `withVoucher` method sets the voucher ID to be used for the transaction. + +**Example:** + +```jsx +const voucherId = '0x...'; // Replace with actual voucher ID +transaction.withVoucher(voucherId); +``` + +### `transactionFee` + +The `transactionFee` method retrieves the transaction fee. + +**Example:** + +```jsx +const fee = await transaction.transactionFee(); +console.log('Transaction fee:', fee.toString()); +``` + +### `signAndSend` +The `signAndSend` method sends the transaction to the chain. This method returns an object containing: + - `msgId`: the ID of the sent message. + - `txHash`: the transaction hash. + - `blockHash`: the hash of the block in which the message is included. + - `isFinalized`: a function to check if the transaction is finalized. + - `response`: a function that can be used to get the response from the program. + +The `response` function returns a promise with the response from the program. If an error occurs during message execution, the promise will be rejected with the error message. + +**Example:** + +```jsx +const { msgId, blockHash, txHash, response, isFinalized } = await transaction.signAndSend(); + +console.log('Message ID:', msgId); +console.log('Transaction hash:', txHash); +console.log('Block hash:', blockHash); + +// Check if the transaction is finalized +const finalized = await isFinalized; +console.log('Is finalized:', finalized); + +// Get the response from the program +try { + const result = await response(); + console.log('Program response:', result); +} catch (error) { + console.error('Error executing message:', error); +} +``` \ No newline at end of file diff --git a/docs/staking/_category_.json b/docs/staking/_category_.json index ce34e29..939ceaf 100644 --- a/docs/staking/_category_.json +++ b/docs/staking/_category_.json @@ -1,4 +1,4 @@ { "label": "Staking", - "position": 12 + "position": 13 } diff --git a/docs/tokenomics/_category_.json b/docs/tokenomics/_category_.json index 82ab07b..bf8f30d 100644 --- a/docs/tokenomics/_category_.json +++ b/docs/tokenomics/_category_.json @@ -1,4 +1,4 @@ { "label": "Tokenomics", - "position": 11 + "position": 12 }