From 48285a0215b057178acf6eeee7b5db2e6d2cdf10 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 22 Jul 2025 16:04:29 +0300 Subject: [PATCH 01/12] Add multisig documentation --- cookbook/cookbook.md | 185 +++++++++++++++++++++++++++++++++++++++++ cookbook/generate.py | 1 + cookbook/multisig.ts | 193 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 379 insertions(+) create mode 100644 cookbook/multisig.ts diff --git a/cookbook/cookbook.md b/cookbook/cookbook.md index bf49734c..c6651531 100644 --- a/cookbook/cookbook.md +++ b/cookbook/cookbook.md @@ -2613,6 +2613,191 @@ Flow for Creating Guarded Relayed Transactions: 4. Then, the guardian signs. 5. Finally, the relayer signs before broadcasting. +### Multisig + +The sdk contains components to interact with the (Multisig Contract)[https://github.com/multiversx/mx-contracts-rs/releases/tag/v0.45.5]. +We can deploy a multisig smart contract, add members, propose and execute actions and query the contract. +The same as the other components, to interact with a multisig smart contract we can use either the MultisigController or the MultisigTransactionsFactory. + +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: +- [`MultisigController`](https://multiversx.github.io/mx-sdk-js-core/v14/classes/MultisigController.html) +- [`MultisigTransactionsFactory`](https://multiversx.github.io/mx-sdk-js-core/v14/classes/MultisigTransactionsFactory.html) + +#### Deploying a Multisig Smart Contract using the controller +```js +{ + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + const bytecode = await loadContractCode("src/testdata/multisig-full.wasm"); + + // create the entrypoint and the multisig controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createMultisigController(abi); + + 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.createTransactionForDeploy(alice, alice.getNonceThenIncrement(), { + quorum: 2, + board: [ + alice.address, + Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + ], + bytecode: bytecode.valueOf(), + gasLimit: 100000000n, + }); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction completion, extract multisig contract's address + const outcome = await controller.awaitCompletedDeploy(txHash); + + const contractAddress = outcome[0].contractAddress; +} +``` + +#### Deploying a Multisig Smart Contract using the factory +```js +{ + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + const bytecode = await loadContractCode("src/testdata/multisig-full.wasm"); + + // create the entrypoint and the multisig factory + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createMultisigTransactionsFactory(abi); + + 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 factory.createTransactionForDeploy(alice.address, { + quorum: 2, + board: [ + alice.address, + Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + ], + bytecode: bytecode.valueOf(), + gasLimit: 100000000n, + }); + + transaction.nonce = alice.getNonceThenIncrement(); + transaction.signature = await alice.signTransaction(transaction); + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); +} +``` + +#### Propose an action using the controller +We'll propose an action to send some EGLD to Carol. After we sent the proposal, we'll also parse the outcome of the transaction to get the `proposal id`. +The id can be later for signing and performing the proposal. + +```js +{ + // create the entrypoint and the multisig controller + const entrypoint = new DevnetEntrypoint(); + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + const controller = entrypoint.createMultisigController(abi); + + 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.createTransactionForProposeTransferExecute( + alice, + alice.getNonceThenIncrement(), + { + multisigContract: contract, + to: Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"), + functionName: "distribute", + functionArguments: [], + gasLimit: 10000000n, + nativeTokenAmount: 1000000000000000000n, + }, + ); + + // sending the transaction + const txHash = await entrypoint.sendTransaction(transaction); + + // parse the outcome and get the proposal id + const actionId = controller.awaitCompletedPerformAction(txHash); +} +``` + +#### Propose an action using the factory +Proposing an action for a multisig contract using the MultisigFactory is very similar to using the controller, but in order to get the proposal id we need to use MultisigTransactionsOutcomeParser. + +```js +{ + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + + // create the entrypoint and the multisig factory + const entrypoint = new DevnetEntrypoint(); + const provider = entrypoint.createNetworkProvider(); + const factory = entrypoint.createMultisigTransactionsFactory(abi); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForProposeTransferExecute(alice.address, { + multisigContract: contract, + to: Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"), + functionName: "distribute", + functionArguments: [], + gasLimit: 10000000n, + nativeTokenAmount: 1000000000000000000n, + }); + // 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); + + // wait for the transaction to execute + const transactionAwaiter = new TransactionWatcher(provider); + transactionAwaiter.awaitCompleted(txHash); + + // parse the outcome of the transaction + const parser = new MultisigTransactionsOutcomeParser({ abi }); + const transactionOnNetwork = await provider.getTransaction(txHash); + const actionId = parser.parseProposeAction(transactionOnNetwork); +} +``` + +#### Querying the Multisig Smart Contract +Unlike creating transactions, querying the multisig can be performed only using the controller. +Let's query the contract to get all board members. + +```js +{ + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + + // create the entrypoint and the multisig controller + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createMultisigController(abi); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const boardMembers = controller.getAllBoardMembers({ multisigAddress: contract.toBech32() }); +} +``` + ### Governance We can create transactions for creating a new governance proposal, vote for a proposal or query the governance contract. diff --git a/cookbook/generate.py b/cookbook/generate.py index 552fccd2..7bab31ce 100644 --- a/cookbook/generate.py +++ b/cookbook/generate.py @@ -15,6 +15,7 @@ current_dir / "delegation.ts", current_dir / "relayed.ts", current_dir / "guarded.ts", + current_dir / "multisig.ts", current_dir / "governance.ts", current_dir / "addresses.ts", current_dir / "wallets.ts", diff --git a/cookbook/multisig.ts b/cookbook/multisig.ts new file mode 100644 index 00000000..d8748b8d --- /dev/null +++ b/cookbook/multisig.ts @@ -0,0 +1,193 @@ +import path from "path"; // md-ignore +import { Account, Address, DevnetEntrypoint, TransactionWatcher } from "../src"; // md-ignore +import { MultisigTransactionsOutcomeParser } from "../src/multisig/multisigTransactionsOutcomeParser"; +import { loadAbiRegistry, loadContractCode } from "../src/testutils"; +// md-start +(async () => { + // ### Multisig + + // The sdk contains components to interact with the (Multisig Contract)[https://github.com/multiversx/mx-contracts-rs/releases/tag/v0.45.5]. + // We can deploy a multisig smart contract, add members, propose and execute actions and query the contract. + // The same as the other components, to interact with a multisig smart contract we can use either the MultisigController or the MultisigTransactionsFactory. + + // 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: + // - `class:MultisigController` + // - `class:MultisigTransactionsFactory` + + // #### Deploying a Multisig Smart Contract using the controller + // ```js + { + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + const bytecode = await loadContractCode("src/testdata/multisig-full.wasm"); + + // create the entrypoint and the multisig controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createMultisigController(abi); + + 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.createTransactionForDeploy(alice, alice.getNonceThenIncrement(), { + quorum: 2, + board: [ + alice.address, + Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + ], + bytecode: bytecode.valueOf(), + gasLimit: 100000000n, + }); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // wait for transaction completion, extract multisig contract's address // md-as-comment + const outcome = await controller.awaitCompletedDeploy(txHash); + + const contractAddress = outcome[0].contractAddress; + } + // ``` + + // #### Deploying a Multisig Smart Contract using the factory + // ```js + { + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + const bytecode = await loadContractCode("src/testdata/multisig-full.wasm"); + + // create the entrypoint and the multisig factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const factory = entrypoint.createMultisigTransactionsFactory(abi); + + 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 factory.createTransactionForDeploy(alice.address, { + quorum: 2, + board: [ + alice.address, + Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"), + ], + bytecode: bytecode.valueOf(), + gasLimit: 100000000n, + }); + + transaction.nonce = alice.getNonceThenIncrement(); + transaction.signature = await alice.signTransaction(transaction); + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + } + // ``` + + // #### Propose an action using the controller + // We'll propose an action to send some EGLD to Carol. After we sent the proposal, we'll also parse the outcome of the transaction to get the `proposal id`. + // The id can be later for signing and performing the proposal. + + // ```js + { + // create the entrypoint and the multisig controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + const controller = entrypoint.createMultisigController(abi); + + 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.createTransactionForProposeTransferExecute( + alice, + alice.getNonceThenIncrement(), + { + multisigContract: contract, + to: Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"), + functionName: "distribute", + functionArguments: [], + gasLimit: 10000000n, + nativeTokenAmount: 1000000000000000000n, + }, + ); + + // sending the transaction // md-as-comment + const txHash = await entrypoint.sendTransaction(transaction); + + // parse the outcome and get the proposal id + const actionId = controller.awaitCompletedPerformAction(txHash); + } + // ``` + + // #### Propose an action using the factory + // Proposing an action for a multisig contract using the MultisigFactory is very similar to using the controller, but in order to get the proposal id we need to use MultisigTransactionsOutcomeParser. + + // ```js + { + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + + // create the entrypoint and the multisig factory // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const provider = entrypoint.createNetworkProvider(); + const factory = entrypoint.createMultisigTransactionsFactory(abi); + + const filePath = path.join("../src", "testdata", "testwallets", "alice.pem"); + const alice = await Account.newFromPem(filePath); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const transaction = await factory.createTransactionForProposeTransferExecute(alice.address, { + multisigContract: contract, + to: Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"), + functionName: "distribute", + functionArguments: [], + gasLimit: 10000000n, + nativeTokenAmount: 1000000000000000000n, + }); + // 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); + + // wait for the transaction to execute + const transactionAwaiter = new TransactionWatcher(provider); + transactionAwaiter.awaitCompleted(txHash); + + // parse the outcome of the transaction + const parser = new MultisigTransactionsOutcomeParser({ abi }); + const transactionOnNetwork = await provider.getTransaction(txHash); + const actionId = parser.parseProposeAction(transactionOnNetwork); + } + // ``` + + // #### Querying the Multisig Smart Contract + // Unlike creating transactions, querying the multisig can be performed only using the controller. + // Let's query the contract to get all board members. + + // ```js + { + const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + + // create the entrypoint and the multisig controller // md-as-comment + const entrypoint = new DevnetEntrypoint(); + const controller = entrypoint.createMultisigController(abi); + + const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); + + const boardMembers = controller.getAllBoardMembers({ multisigAddress: contract.toBech32() }); + } + // ``` +})().catch((e) => { + console.log({ e }); +}); From 265341660d47e9997f55afbe61506d26f638a017 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 17 Jul 2025 10:26:47 +0300 Subject: [PATCH 02/12] set default values for optional args --- package-lock.json | 4 ++-- package.json | 2 +- src/multisig/resources.ts | 19 +++++++++---------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index e59a42bc..5f22efdc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@multiversx/sdk-core", - "version": "14.2.7", + "version": "14.2.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@multiversx/sdk-core", - "version": "14.2.7", + "version": "14.2.8", "license": "MIT", "dependencies": { "@multiversx/sdk-transaction-decoder": "1.0.2", diff --git a/package.json b/package.json index 0749140f..d5a130c1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@multiversx/sdk-core", - "version": "14.2.7", + "version": "14.2.8", "description": "MultiversX SDK for JavaScript and TypeScript", "author": "MultiversX", "homepage": "https://multiversx.com", diff --git a/src/multisig/resources.ts b/src/multisig/resources.ts index 2e3b0c87..12dccd20 100644 --- a/src/multisig/resources.ts +++ b/src/multisig/resources.ts @@ -174,7 +174,7 @@ export class ChangeQuorum extends MultisigAction { export class SendTransferExecuteEgld extends MultisigAction { receiver: Address; amount: bigint; - optionalGasLimit?: bigint; + optionalGasLimit: bigint; functionName: string; arguments: Uint8Array[]; @@ -184,14 +184,14 @@ export class SendTransferExecuteEgld extends MultisigAction { this.receiver = data.to; this.amount = BigInt(data.egld_amount?.toFixed() ?? 0); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); - this.functionName = data.endpoint_name.toString(); - this.arguments = data.arguments; + this.functionName = data.endpoint_name?.toString() ?? ""; + this.arguments = data.arguments ?? new Uint8Array(); } } export class SendTransferExecuteEsdt extends MultisigAction { receiver: Address; tokens: TokenTransfer[]; - optionalGasLimit?: bigint; + optionalGasLimit: bigint; funcionName: string; arguments: Uint8Array[]; @@ -206,17 +206,16 @@ export class SendTransferExecuteEsdt extends MultisigAction { amount: token.amount, }), ); - this.optionalGasLimit = BigInt(data.opt_gas_limit.toFixed()); - - this.funcionName = Buffer.from(data.endpoint_name.toString(), "hex").toString(); - this.arguments = data.arguments; + this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); + this.funcionName = Buffer.from(data.endpoint_name?.toString() ?? "", "hex").toString(); + this.arguments = data.arguments ?? new Uint8Array(); } } export class SendAsyncCall extends MultisigAction { receiver: Address; amount: bigint; - optionalGasLimit?: bigint; + optionalGasLimit: bigint; funcionName: string; arguments: Uint8Array[]; @@ -227,7 +226,7 @@ export class SendAsyncCall extends MultisigAction { this.amount = BigInt(data.egld_amount?.toFixed() ?? 0); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); this.funcionName = data.endpoint_name.toString(); - this.arguments = data.arguments; + this.arguments = data.arguments ?? new Uint8Array(); } } From 092783547a4149072b255f571bc21b9bb2f8f328 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 17 Jul 2025 10:32:29 +0300 Subject: [PATCH 03/12] fix typos --- src/multisig/multisigController.spec.ts | 4 ++-- src/multisig/resources.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/multisig/multisigController.spec.ts b/src/multisig/multisigController.spec.ts index 971c674f..be9f37da 100644 --- a/src/multisig/multisigController.spec.ts +++ b/src/multisig/multisigController.spec.ts @@ -384,7 +384,7 @@ describe("test multisig controller query methods", () => { const mappedRes = result as resources.SendAsyncCall; assert.equal(mappedRes.receiver.toBech32(), "erd1qqqqqqqqqqqqqpgq0rffvv4vk9vesqplv9ws55fxzdfaspqa8cfszy2hms"); - assert.equal(mappedRes.funcionName, "add"); + assert.equal(mappedRes.functionName, "add"); assert.equal(mappedRes.amount, 0n); }); @@ -412,7 +412,7 @@ describe("test multisig controller query methods", () => { const mappedRes = result as resources.SendTransferExecuteEsdt; assert.equal(mappedRes.receiver.toBech32(), "erd1qqqqqqqqqqqqqpgqfxlljcaalgl2qfcnxcsftheju0ts36kvl3ts3qkewe"); - assert.equal(mappedRes.funcionName, "distribute"); + assert.equal(mappedRes.functionName, "distribute"); }); it("getActionData returns the action data as AddBoardMember", async function () { diff --git a/src/multisig/resources.ts b/src/multisig/resources.ts index 12dccd20..179102a4 100644 --- a/src/multisig/resources.ts +++ b/src/multisig/resources.ts @@ -192,7 +192,7 @@ export class SendTransferExecuteEsdt extends MultisigAction { receiver: Address; tokens: TokenTransfer[]; optionalGasLimit: bigint; - funcionName: string; + functionName: string; arguments: Uint8Array[]; constructor(data: any) { @@ -207,7 +207,7 @@ export class SendTransferExecuteEsdt extends MultisigAction { }), ); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); - this.funcionName = Buffer.from(data.endpoint_name?.toString() ?? "", "hex").toString(); + this.functionName = Buffer.from(data.endpoint_name?.toString() ?? "", "hex").toString(); this.arguments = data.arguments ?? new Uint8Array(); } } @@ -216,7 +216,7 @@ export class SendAsyncCall extends MultisigAction { receiver: Address; amount: bigint; optionalGasLimit: bigint; - funcionName: string; + functionName: string; arguments: Uint8Array[]; constructor(data: any) { @@ -225,7 +225,7 @@ export class SendAsyncCall extends MultisigAction { this.receiver = data.to; this.amount = BigInt(data.egld_amount?.toFixed() ?? 0); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); - this.funcionName = data.endpoint_name.toString(); + this.functionName = data.endpoint_name.toString(); this.arguments = data.arguments ?? new Uint8Array(); } } From 65cf7ddb3c2418c1c5f226fc2654e718b6787f9e Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 17 Jul 2025 10:37:43 +0300 Subject: [PATCH 04/12] fix args default --- src/multisig/resources.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/multisig/resources.ts b/src/multisig/resources.ts index 179102a4..660fb7eb 100644 --- a/src/multisig/resources.ts +++ b/src/multisig/resources.ts @@ -185,7 +185,7 @@ export class SendTransferExecuteEgld extends MultisigAction { this.amount = BigInt(data.egld_amount?.toFixed() ?? 0); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); this.functionName = data.endpoint_name?.toString() ?? ""; - this.arguments = data.arguments ?? new Uint8Array(); + this.arguments = data.arguments ?? []; } } export class SendTransferExecuteEsdt extends MultisigAction { @@ -208,7 +208,7 @@ export class SendTransferExecuteEsdt extends MultisigAction { ); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); this.functionName = Buffer.from(data.endpoint_name?.toString() ?? "", "hex").toString(); - this.arguments = data.arguments ?? new Uint8Array(); + this.arguments = data.arguments ?? []; } } @@ -226,7 +226,7 @@ export class SendAsyncCall extends MultisigAction { this.amount = BigInt(data.egld_amount?.toFixed() ?? 0); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); this.functionName = data.endpoint_name.toString(); - this.arguments = data.arguments ?? new Uint8Array(); + this.arguments = data.arguments ?? []; } } From 12cc1fa04ec174475461d4ee982e31db247d1200 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 17 Jul 2025 10:26:47 +0300 Subject: [PATCH 05/12] set default values for optional args --- src/multisig/resources.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/multisig/resources.ts b/src/multisig/resources.ts index 660fb7eb..12dccd20 100644 --- a/src/multisig/resources.ts +++ b/src/multisig/resources.ts @@ -185,14 +185,14 @@ export class SendTransferExecuteEgld extends MultisigAction { this.amount = BigInt(data.egld_amount?.toFixed() ?? 0); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); this.functionName = data.endpoint_name?.toString() ?? ""; - this.arguments = data.arguments ?? []; + this.arguments = data.arguments ?? new Uint8Array(); } } export class SendTransferExecuteEsdt extends MultisigAction { receiver: Address; tokens: TokenTransfer[]; optionalGasLimit: bigint; - functionName: string; + funcionName: string; arguments: Uint8Array[]; constructor(data: any) { @@ -207,8 +207,8 @@ export class SendTransferExecuteEsdt extends MultisigAction { }), ); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); - this.functionName = Buffer.from(data.endpoint_name?.toString() ?? "", "hex").toString(); - this.arguments = data.arguments ?? []; + this.funcionName = Buffer.from(data.endpoint_name?.toString() ?? "", "hex").toString(); + this.arguments = data.arguments ?? new Uint8Array(); } } @@ -216,7 +216,7 @@ export class SendAsyncCall extends MultisigAction { receiver: Address; amount: bigint; optionalGasLimit: bigint; - functionName: string; + funcionName: string; arguments: Uint8Array[]; constructor(data: any) { @@ -225,8 +225,8 @@ export class SendAsyncCall extends MultisigAction { this.receiver = data.to; this.amount = BigInt(data.egld_amount?.toFixed() ?? 0); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); - this.functionName = data.endpoint_name.toString(); - this.arguments = data.arguments ?? []; + this.funcionName = data.endpoint_name.toString(); + this.arguments = data.arguments ?? new Uint8Array(); } } From 001a0ddd92249f6aab31d67481df227cf3b47bba Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 17 Jul 2025 10:32:29 +0300 Subject: [PATCH 06/12] fix typos --- src/multisig/resources.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/multisig/resources.ts b/src/multisig/resources.ts index 12dccd20..179102a4 100644 --- a/src/multisig/resources.ts +++ b/src/multisig/resources.ts @@ -192,7 +192,7 @@ export class SendTransferExecuteEsdt extends MultisigAction { receiver: Address; tokens: TokenTransfer[]; optionalGasLimit: bigint; - funcionName: string; + functionName: string; arguments: Uint8Array[]; constructor(data: any) { @@ -207,7 +207,7 @@ export class SendTransferExecuteEsdt extends MultisigAction { }), ); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); - this.funcionName = Buffer.from(data.endpoint_name?.toString() ?? "", "hex").toString(); + this.functionName = Buffer.from(data.endpoint_name?.toString() ?? "", "hex").toString(); this.arguments = data.arguments ?? new Uint8Array(); } } @@ -216,7 +216,7 @@ export class SendAsyncCall extends MultisigAction { receiver: Address; amount: bigint; optionalGasLimit: bigint; - funcionName: string; + functionName: string; arguments: Uint8Array[]; constructor(data: any) { @@ -225,7 +225,7 @@ export class SendAsyncCall extends MultisigAction { this.receiver = data.to; this.amount = BigInt(data.egld_amount?.toFixed() ?? 0); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); - this.funcionName = data.endpoint_name.toString(); + this.functionName = data.endpoint_name.toString(); this.arguments = data.arguments ?? new Uint8Array(); } } From db8d6228127e91860cefdc2db92aeeda752906c8 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Thu, 17 Jul 2025 10:37:43 +0300 Subject: [PATCH 07/12] fix args default --- src/multisig/resources.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/multisig/resources.ts b/src/multisig/resources.ts index 179102a4..660fb7eb 100644 --- a/src/multisig/resources.ts +++ b/src/multisig/resources.ts @@ -185,7 +185,7 @@ export class SendTransferExecuteEgld extends MultisigAction { this.amount = BigInt(data.egld_amount?.toFixed() ?? 0); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); this.functionName = data.endpoint_name?.toString() ?? ""; - this.arguments = data.arguments ?? new Uint8Array(); + this.arguments = data.arguments ?? []; } } export class SendTransferExecuteEsdt extends MultisigAction { @@ -208,7 +208,7 @@ export class SendTransferExecuteEsdt extends MultisigAction { ); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); this.functionName = Buffer.from(data.endpoint_name?.toString() ?? "", "hex").toString(); - this.arguments = data.arguments ?? new Uint8Array(); + this.arguments = data.arguments ?? []; } } @@ -226,7 +226,7 @@ export class SendAsyncCall extends MultisigAction { this.amount = BigInt(data.egld_amount?.toFixed() ?? 0); this.optionalGasLimit = BigInt(data.opt_gas_limit?.toFixed() ?? 0); this.functionName = data.endpoint_name.toString(); - this.arguments = data.arguments ?? new Uint8Array(); + this.arguments = data.arguments ?? []; } } From 169b6ffa12f2f2147e8ed1d7a2fd040d57500146 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 22 Jul 2025 16:31:52 +0300 Subject: [PATCH 08/12] Fic proposeTransferExecute --- .../multisigTransactionsFactory.spec.ts | 24 +++++++++++++++++++ src/multisig/multisigTransactionsFactory.ts | 22 ++++++++++------- .../proposeTransferExecuteContractInput.ts | 2 +- src/multisig/resources.ts | 4 ++-- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/multisig/multisigTransactionsFactory.spec.ts b/src/multisig/multisigTransactionsFactory.spec.ts index 068a05dd..d93e7aee 100644 --- a/src/multisig/multisigTransactionsFactory.spec.ts +++ b/src/multisig/multisigTransactionsFactory.spec.ts @@ -174,6 +174,30 @@ describe("test multisig transactions factory", function () { ); }); + it("should create transaction for propose transfer execute with EGLD send", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqpgq0rffvv4vk9vesqplv9ws55fxzdfaspqa8cfszy2hms", + ); + const amount = 1000000000000000000n; // 1 EGLD + const transaction = factory.createTransactionForProposeTransferExecute(senderAddress, { + multisigContract: multisigContractAddress, + gasLimit: 60_000_000n, + nativeTokenAmount: amount, + to: multisigContractAddress, + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.equal(transaction.chainID, config.chainID); + assert.deepEqual( + transaction.data.toString(), + "proposeTransferExecute@0000000000000000050078d29632acb15998003f615d0a51261353d8041d3e13@0de0b6b3a7640000@", + ); + }); + it("should create transaction for propose transfer execute ESDT", function () { const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); const destinationContract = Address.newFromBech32( diff --git a/src/multisig/multisigTransactionsFactory.ts b/src/multisig/multisigTransactionsFactory.ts index c2997776..cf25aee2 100644 --- a/src/multisig/multisigTransactionsFactory.ts +++ b/src/multisig/multisigTransactionsFactory.ts @@ -124,14 +124,18 @@ export class MultisigTransactionsFactory { sender: Address, options: resources.ProposeTransferExecuteInput, ): Transaction { - const gasOption = new U64Value(options.optGasLimit ?? 0n); - const input = ProposeTransferExecuteContractInput.newFromTransferExecuteInput({ - multisig: options.multisigContract, - to: options.to, - functionName: options.functionName, - arguments: options.functionArguments, - abi: options.abi, - }); + const gasOption = options.optGasLimit ? new U64Value(options.optGasLimit) : null; + let functionCall = []; + if (options.functionName) { + const input = ProposeTransferExecuteContractInput.newFromTransferExecuteInput({ + multisig: options.multisigContract, + to: options.to, + functionName: options.functionName, + arguments: options.functionArguments, + abi: options.abi, + }); + functionCall = input.functionCall; + } return this.smartContractFactory.createTransactionForExecute(sender, { contract: options.multisigContract, @@ -141,7 +145,7 @@ export class MultisigTransactionsFactory { new AddressValue(options.to), new BigUIntValue(options.nativeTokenAmount), new OptionValue(new OptionType(new U64Type()), gasOption), - VariadicValue.fromItems(...input.functionCall.map((value) => new BytesValue(value))), + VariadicValue.fromItems(...functionCall.map((value) => new BytesValue(value))), ], }); } diff --git a/src/multisig/proposeTransferExecuteContractInput.ts b/src/multisig/proposeTransferExecuteContractInput.ts index 6c89b6cb..3ae252f3 100644 --- a/src/multisig/proposeTransferExecuteContractInput.ts +++ b/src/multisig/proposeTransferExecuteContractInput.ts @@ -21,7 +21,7 @@ export class ProposeTransferExecuteContractInput { multisig: Address; to: Address; functionName: string; - arguments: any[]; + arguments?: any[]; optGasLimit?: bigint; abi?: Abi; }): ProposeTransferExecuteContractInput { diff --git a/src/multisig/resources.ts b/src/multisig/resources.ts index 660fb7eb..edc9ee73 100644 --- a/src/multisig/resources.ts +++ b/src/multisig/resources.ts @@ -39,8 +39,8 @@ export type ProposeTransferExecuteInput = MultisigContractInput & { to: Address; nativeTokenAmount: bigint; optGasLimit?: bigint; - functionName: string; - functionArguments: any[]; + functionName?: string; + functionArguments?: any[]; abi?: Abi; }; From 729b99b984101cc8173bb0efef727a3d295aef75 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 22 Jul 2025 16:33:23 +0300 Subject: [PATCH 09/12] bump version and fix critical version --- package-lock.json | 47 +++++++++++++++++++++++++++++++++++++++-------- package.json | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5f22efdc..90590dc9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@multiversx/sdk-core", - "version": "14.2.8", + "version": "14.2.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@multiversx/sdk-core", - "version": "14.2.8", + "version": "14.2.9", "license": "MIT", "dependencies": { "@multiversx/sdk-transaction-decoder": "1.0.2", @@ -2004,6 +2004,21 @@ "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==", + "optional": true, + "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/es6-object-assign": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", @@ -2604,13 +2619,15 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "optional": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -6965,6 +6982,18 @@ "es-errors": "^1.3.0" } }, + "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==", + "optional": true, + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, "es6-object-assign": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", @@ -7393,13 +7422,15 @@ } }, "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "optional": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, diff --git a/package.json b/package.json index d5a130c1..a93e045d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@multiversx/sdk-core", - "version": "14.2.8", + "version": "14.2.9", "description": "MultiversX SDK for JavaScript and TypeScript", "author": "MultiversX", "homepage": "https://multiversx.com", From f44de48a3387690ef1ed45323738e16c0b4276cc Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 23 Jul 2025 11:51:09 +0300 Subject: [PATCH 10/12] Update multisig docs --- cookbook/cookbook.md | 4 ---- cookbook/multisig.ts | 4 ---- 2 files changed, 8 deletions(-) diff --git a/cookbook/cookbook.md b/cookbook/cookbook.md index c6651531..cc0487ce 100644 --- a/cookbook/cookbook.md +++ b/cookbook/cookbook.md @@ -2717,8 +2717,6 @@ The id can be later for signing and performing the proposal. { multisigContract: contract, to: Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"), - functionName: "distribute", - functionArguments: [], gasLimit: 10000000n, nativeTokenAmount: 1000000000000000000n, }, @@ -2752,8 +2750,6 @@ Proposing an action for a multisig contract using the MultisigFactory is very si const transaction = await factory.createTransactionForProposeTransferExecute(alice.address, { multisigContract: contract, to: Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"), - functionName: "distribute", - functionArguments: [], gasLimit: 10000000n, nativeTokenAmount: 1000000000000000000n, }); diff --git a/cookbook/multisig.ts b/cookbook/multisig.ts index d8748b8d..f1eee9e0 100644 --- a/cookbook/multisig.ts +++ b/cookbook/multisig.ts @@ -108,8 +108,6 @@ import { loadAbiRegistry, loadContractCode } from "../src/testutils"; { multisigContract: contract, to: Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"), - functionName: "distribute", - functionArguments: [], gasLimit: 10000000n, nativeTokenAmount: 1000000000000000000n, }, @@ -143,8 +141,6 @@ import { loadAbiRegistry, loadContractCode } from "../src/testutils"; const transaction = await factory.createTransactionForProposeTransferExecute(alice.address, { multisigContract: contract, to: Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"), - functionName: "distribute", - functionArguments: [], gasLimit: 10000000n, nativeTokenAmount: 1000000000000000000n, }); From f2def197b6ccc1700fc713d601bd134edc43e950 Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 23 Jul 2025 12:10:32 +0300 Subject: [PATCH 11/12] Code review follow up --- cookbook/cookbook.md | 11 +++++------ cookbook/multisig.ts | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/cookbook/cookbook.md b/cookbook/cookbook.md index cc0487ce..b72d1783 100644 --- a/cookbook/cookbook.md +++ b/cookbook/cookbook.md @@ -2615,7 +2615,7 @@ Flow for Creating Guarded Relayed Transactions: ### Multisig -The sdk contains components to interact with the (Multisig Contract)[https://github.com/multiversx/mx-contracts-rs/releases/tag/v0.45.5]. +The sdk contains components to interact with the [Multisig Contract](https://github.com/multiversx/mx-contracts-rs/releases/tag/v0.45.5). We can deploy a multisig smart contract, add members, propose and execute actions and query the contract. The same as the other components, to interact with a multisig smart contract we can use either the MultisigController or the MultisigTransactionsFactory. @@ -2694,7 +2694,7 @@ These operations can be performed using both the **controller** and the **factor #### Propose an action using the controller We'll propose an action to send some EGLD to Carol. After we sent the proposal, we'll also parse the outcome of the transaction to get the `proposal id`. -The id can be later for signing and performing the proposal. +The id can be used later for signing and performing the proposal. ```js { @@ -2731,7 +2731,7 @@ The id can be later for signing and performing the proposal. ``` #### Propose an action using the factory -Proposing an action for a multisig contract using the MultisigFactory is very similar to using the controller, but in order to get the proposal id we need to use MultisigTransactionsOutcomeParser. +Proposing an action for a multisig contract using the MultisigFactory is very similar to using the controller, but in order to get the proposal id, we need to use MultisigTransactionsOutcomeParser. ```js { @@ -2767,11 +2767,10 @@ Proposing an action for a multisig contract using the MultisigFactory is very si // wait for the transaction to execute const transactionAwaiter = new TransactionWatcher(provider); - transactionAwaiter.awaitCompleted(txHash); + const transactionOnNetwork = await transactionAwaiter.awaitCompleted(txHash); // parse the outcome of the transaction const parser = new MultisigTransactionsOutcomeParser({ abi }); - const transactionOnNetwork = await provider.getTransaction(txHash); const actionId = parser.parseProposeAction(transactionOnNetwork); } ``` @@ -2790,7 +2789,7 @@ Let's query the contract to get all board members. const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - const boardMembers = controller.getAllBoardMembers({ multisigAddress: contract.toBech32() }); + const boardMembers = await controller.getAllBoardMembers({ multisigAddress: contract.toBech32() }); } ``` diff --git a/cookbook/multisig.ts b/cookbook/multisig.ts index f1eee9e0..2059de2e 100644 --- a/cookbook/multisig.ts +++ b/cookbook/multisig.ts @@ -6,7 +6,7 @@ import { loadAbiRegistry, loadContractCode } from "../src/testutils"; (async () => { // ### Multisig - // The sdk contains components to interact with the (Multisig Contract)[https://github.com/multiversx/mx-contracts-rs/releases/tag/v0.45.5]. + // The sdk contains components to interact with the [Multisig Contract](https://github.com/multiversx/mx-contracts-rs/releases/tag/v0.45.5). // We can deploy a multisig smart contract, add members, propose and execute actions and query the contract. // The same as the other components, to interact with a multisig smart contract we can use either the MultisigController or the MultisigTransactionsFactory. @@ -85,7 +85,7 @@ import { loadAbiRegistry, loadContractCode } from "../src/testutils"; // #### Propose an action using the controller // We'll propose an action to send some EGLD to Carol. After we sent the proposal, we'll also parse the outcome of the transaction to get the `proposal id`. - // The id can be later for signing and performing the proposal. + // The id can be used later for signing and performing the proposal. // ```js { @@ -122,7 +122,7 @@ import { loadAbiRegistry, loadContractCode } from "../src/testutils"; // ``` // #### Propose an action using the factory - // Proposing an action for a multisig contract using the MultisigFactory is very similar to using the controller, but in order to get the proposal id we need to use MultisigTransactionsOutcomeParser. + // Proposing an action for a multisig contract using the MultisigFactory is very similar to using the controller, but in order to get the proposal id, we need to use MultisigTransactionsOutcomeParser. // ```js { @@ -158,11 +158,10 @@ import { loadAbiRegistry, loadContractCode } from "../src/testutils"; // wait for the transaction to execute const transactionAwaiter = new TransactionWatcher(provider); - transactionAwaiter.awaitCompleted(txHash); + const transactionOnNetwork = await transactionAwaiter.awaitCompleted(txHash); // parse the outcome of the transaction const parser = new MultisigTransactionsOutcomeParser({ abi }); - const transactionOnNetwork = await provider.getTransaction(txHash); const actionId = parser.parseProposeAction(transactionOnNetwork); } // ``` @@ -181,7 +180,7 @@ import { loadAbiRegistry, loadContractCode } from "../src/testutils"; const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva"); - const boardMembers = controller.getAllBoardMembers({ multisigAddress: contract.toBech32() }); + const boardMembers = await controller.getAllBoardMembers({ multisigAddress: contract.toBech32() }); } // ``` })().catch((e) => { From e0f29d05a31db1e0165a44e9e0a6aa08d8592121 Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 23 Jul 2025 12:32:21 +0300 Subject: [PATCH 12/12] Add missing await --- cookbook/cookbook.md | 2 +- cookbook/multisig.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/cookbook.md b/cookbook/cookbook.md index b72d1783..ac991db3 100644 --- a/cookbook/cookbook.md +++ b/cookbook/cookbook.md @@ -2726,7 +2726,7 @@ The id can be used later for signing and performing the proposal. const txHash = await entrypoint.sendTransaction(transaction); // parse the outcome and get the proposal id - const actionId = controller.awaitCompletedPerformAction(txHash); + const actionId = await controller.awaitCompletedPerformAction(txHash); } ``` diff --git a/cookbook/multisig.ts b/cookbook/multisig.ts index 2059de2e..c76ef873 100644 --- a/cookbook/multisig.ts +++ b/cookbook/multisig.ts @@ -117,7 +117,7 @@ import { loadAbiRegistry, loadContractCode } from "../src/testutils"; const txHash = await entrypoint.sendTransaction(transaction); // parse the outcome and get the proposal id - const actionId = controller.awaitCompletedPerformAction(txHash); + const actionId = await controller.awaitCompletedPerformAction(txHash); } // ```