Skip to content

Commit 834dd2d

Browse files
authored
Merge pull request #1136 from multiversx/TOOL-655-update-cookbook-for-v14-js
Add multisig and governance to v14 docs
2 parents d982676 + 1272878 commit 834dd2d

File tree

1 file changed

+346
-0
lines changed

1 file changed

+346
-0
lines changed

docs/sdk-and-tools/sdk-js/sdk-js-cookbook-v14.md

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2626,6 +2626,352 @@ Flow for Creating Guarded Relayed Transactions:
26262626
4. Then, the guardian signs.
26272627
5. Finally, the relayer signs before broadcasting.
26282628

2629+
### Multisig
2630+
2631+
The sdk contains components to interact with the [Multisig Contract](https://github.com/multiversx/mx-contracts-rs/releases/tag/v0.45.5).
2632+
We can deploy a multisig smart contract, add members, propose and execute actions and query the contract.
2633+
The same as the other components, to interact with a multisig smart contract we can use either the MultisigController or the MultisigTransactionsFactory.
2634+
2635+
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:
2636+
- [`MultisigController`](https://multiversx.github.io/mx-sdk-js-core/v14/classes/MultisigController.html)
2637+
- [`MultisigTransactionsFactory`](https://multiversx.github.io/mx-sdk-js-core/v14/classes/MultisigTransactionsFactory.html)
2638+
2639+
#### Deploying a Multisig Smart Contract using the controller
2640+
```js
2641+
{
2642+
const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json");
2643+
const bytecode = await loadContractCode("src/testdata/multisig-full.wasm");
2644+
2645+
// create the entrypoint and the multisig controller
2646+
const entrypoint = new DevnetEntrypoint();
2647+
const controller = entrypoint.createMultisigController(abi);
2648+
2649+
const filePath = path.join("../src", "testdata", "testwallets", "alice.pem");
2650+
const alice = await Account.newFromPem(filePath);
2651+
2652+
// fetch the nonce of the network
2653+
alice.nonce = await entrypoint.recallAccountNonce(alice.address);
2654+
2655+
const transaction = await controller.createTransactionForDeploy(alice, alice.getNonceThenIncrement(), {
2656+
quorum: 2,
2657+
board: [
2658+
alice.address,
2659+
Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"),
2660+
],
2661+
bytecode: bytecode.valueOf(),
2662+
gasLimit: 100000000n,
2663+
});
2664+
2665+
// sending the transaction
2666+
const txHash = await entrypoint.sendTransaction(transaction);
2667+
2668+
// wait for transaction completion, extract multisig contract's address
2669+
const outcome = await controller.awaitCompletedDeploy(txHash);
2670+
2671+
const contractAddress = outcome[0].contractAddress;
2672+
}
2673+
```
2674+
2675+
#### Deploying a Multisig Smart Contract using the factory
2676+
```js
2677+
{
2678+
const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json");
2679+
const bytecode = await loadContractCode("src/testdata/multisig-full.wasm");
2680+
2681+
// create the entrypoint and the multisig factory
2682+
const entrypoint = new DevnetEntrypoint();
2683+
const factory = entrypoint.createMultisigTransactionsFactory(abi);
2684+
2685+
const filePath = path.join("../src", "testdata", "testwallets", "alice.pem");
2686+
const alice = await Account.newFromPem(filePath);
2687+
2688+
// fetch the nonce of the network
2689+
alice.nonce = await entrypoint.recallAccountNonce(alice.address);
2690+
2691+
const transaction = factory.createTransactionForDeploy(alice.address, {
2692+
quorum: 2,
2693+
board: [
2694+
alice.address,
2695+
Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"),
2696+
],
2697+
bytecode: bytecode.valueOf(),
2698+
gasLimit: 100000000n,
2699+
});
2700+
2701+
transaction.nonce = alice.getNonceThenIncrement();
2702+
transaction.signature = await alice.signTransaction(transaction);
2703+
// sending the transaction
2704+
const txHash = await entrypoint.sendTransaction(transaction);
2705+
}
2706+
```
2707+
2708+
#### Propose an action using the controller
2709+
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`.
2710+
The id can be used later for signing and performing the proposal.
2711+
2712+
```js
2713+
{
2714+
// create the entrypoint and the multisig controller
2715+
const entrypoint = new DevnetEntrypoint();
2716+
const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json");
2717+
const controller = entrypoint.createMultisigController(abi);
2718+
2719+
const filePath = path.join("../src", "testdata", "testwallets", "alice.pem");
2720+
const alice = await Account.newFromPem(filePath);
2721+
2722+
// fetch the nonce of the network
2723+
alice.nonce = await entrypoint.recallAccountNonce(alice.address);
2724+
2725+
const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva");
2726+
2727+
const transaction = await controller.createTransactionForProposeTransferExecute(
2728+
alice,
2729+
alice.getNonceThenIncrement(),
2730+
{
2731+
multisigContract: contract,
2732+
to: Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"),
2733+
gasLimit: 10000000n,
2734+
nativeTokenAmount: 1000000000000000000n,
2735+
},
2736+
);
2737+
2738+
// sending the transaction
2739+
const txHash = await entrypoint.sendTransaction(transaction);
2740+
2741+
// parse the outcome and get the proposal id
2742+
const actionId = await controller.awaitCompletedPerformAction(txHash);
2743+
}
2744+
```
2745+
2746+
#### Propose an action using the factory
2747+
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.
2748+
2749+
```js
2750+
{
2751+
const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json");
2752+
2753+
// create the entrypoint and the multisig factory
2754+
const entrypoint = new DevnetEntrypoint();
2755+
const provider = entrypoint.createNetworkProvider();
2756+
const factory = entrypoint.createMultisigTransactionsFactory(abi);
2757+
2758+
const filePath = path.join("../src", "testdata", "testwallets", "alice.pem");
2759+
const alice = await Account.newFromPem(filePath);
2760+
2761+
const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva");
2762+
2763+
const transaction = factory.createTransactionForProposeTransferExecute(alice.address, {
2764+
multisigContract: contract,
2765+
to: Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"),
2766+
gasLimit: 10000000n,
2767+
nativeTokenAmount: 1000000000000000000n,
2768+
});
2769+
// fetch the nonce of the network
2770+
alice.nonce = await entrypoint.recallAccountNonce(alice.address);
2771+
2772+
// set the nonce
2773+
transaction.nonce = alice.getNonceThenIncrement();
2774+
2775+
// sign the transaction
2776+
transaction.signature = await alice.signTransaction(transaction);
2777+
2778+
// sending the transaction
2779+
const txHash = await entrypoint.sendTransaction(transaction);
2780+
2781+
// wait for the transaction to execute
2782+
const transactionAwaiter = new TransactionWatcher(provider);
2783+
const transactionOnNetwork = await transactionAwaiter.awaitCompleted(txHash);
2784+
2785+
// parse the outcome of the transaction
2786+
const parser = new MultisigTransactionsOutcomeParser({ abi });
2787+
const actionId = parser.parseProposeAction(transactionOnNetwork);
2788+
}
2789+
```
2790+
2791+
#### Querying the Multisig Smart Contract
2792+
Unlike creating transactions, querying the multisig can be performed only using the controller.
2793+
Let's query the contract to get all board members.
2794+
2795+
```js
2796+
{
2797+
const abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json");
2798+
2799+
// create the entrypoint and the multisig controller
2800+
const entrypoint = new DevnetEntrypoint();
2801+
const controller = entrypoint.createMultisigController(abi);
2802+
2803+
const contract = Address.newFromBech32("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf8llllswuedva");
2804+
2805+
const boardMembers = await controller.getAllBoardMembers({ multisigAddress: contract.toBech32() });
2806+
}
2807+
```
2808+
2809+
### Governance
2810+
2811+
We can create transactions for creating a new governance proposal, vote for a proposal or query the governance contract.
2812+
2813+
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:
2814+
- [`GovernanceController`](https://multiversx.github.io/mx-sdk-js-core/v14/classes/GovernanceController.html)
2815+
- [`GovernanceTransactionsFactory`](https://multiversx.github.io/mx-sdk-js-core/v14/classes/GovernanceTransactionsFactory.html)
2816+
2817+
#### Creating a new proposal using the controller
2818+
```js
2819+
{
2820+
// create the entrypoint and the governance controller
2821+
const entrypoint = new DevnetEntrypoint();
2822+
const controller = entrypoint.createGovernanceController();
2823+
2824+
const filePath = path.join("../src", "testdata", "testwallets", "alice.pem");
2825+
const alice = await Account.newFromPem(filePath);
2826+
2827+
// fetch the nonce of the network
2828+
alice.nonce = await entrypoint.recallAccountNonce(alice.address);
2829+
const commitHash = "1db734c0315f9ec422b88f679ccfe3e0197b9d67";
2830+
2831+
const transaction = await controller.createTransactionForNewProposal(alice, alice.getNonceThenIncrement(), {
2832+
commitHash: commitHash,
2833+
startVoteEpoch: 10,
2834+
endVoteEpoch: 15,
2835+
nativeTokenAmount: 500_000000000000000000n,
2836+
});
2837+
2838+
// sending the transaction
2839+
const txHash = await entrypoint.sendTransaction(transaction);
2840+
2841+
// wait for transaction completion, extract proposal's details
2842+
const outcome = await controller.awaitCompletedProposeProposal(txHash);
2843+
2844+
const proposalNonce = outcome[0].proposalNonce;
2845+
const proposalCommitHash = outcome[0].commitHash;
2846+
const proposalStartVoteEpoch = outcome[0].startVoteEpoch;
2847+
const proposalEndVoteEpoch = outcome[0].endVoteEpoch;
2848+
}
2849+
```
2850+
2851+
#### Creating a new proposal using the factory
2852+
```js
2853+
{
2854+
// create the entrypoint and the governance factory
2855+
const entrypoint = new DevnetEntrypoint();
2856+
const factory = entrypoint.createGovernanceTransactionsFactory();
2857+
2858+
const filePath = path.join("../src", "testdata", "testwallets", "alice.pem");
2859+
const alice = await Account.newFromPem(filePath);
2860+
2861+
const commitHash = "1db734c0315f9ec422b88f679ccfe3e0197b9d67";
2862+
2863+
const transaction = factory.createTransactionForNewProposal(alice.address, {
2864+
commitHash: commitHash,
2865+
startVoteEpoch: 10,
2866+
endVoteEpoch: 15,
2867+
nativeTokenAmount: 500_000000000000000000n,
2868+
});
2869+
// fetch the nonce of the network
2870+
alice.nonce = await entrypoint.recallAccountNonce(alice.address);
2871+
2872+
// set the nonce
2873+
transaction.nonce = alice.getNonceThenIncrement();
2874+
2875+
// sign the transaction
2876+
transaction.signature = await alice.signTransaction(transaction);
2877+
2878+
// sending the transaction
2879+
const txHash = await entrypoint.sendTransaction(transaction);
2880+
2881+
// waits until the transaction is processed and fetches it from the network
2882+
const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash);
2883+
2884+
const parser = new GovernanceTransactionsOutcomeParser({});
2885+
const outcome = parser.parseNewProposal(transactionOnNetwork);
2886+
const proposalNonce = outcome[0].proposalNonce;
2887+
const proposalCommitHash = outcome[0].commitHash;
2888+
const proposalStartVoteEpoch = outcome[0].startVoteEpoch;
2889+
const proposalEndVoteEpoch = outcome[0].endVoteEpoch;
2890+
}
2891+
```
2892+
2893+
#### Vote for a proposal using the controller
2894+
2895+
```js
2896+
{
2897+
// create the entrypoint and the governance controller
2898+
const entrypoint = new DevnetEntrypoint();
2899+
const controller = entrypoint.createGovernanceController();
2900+
2901+
const filePath = path.join("../src", "testdata", "testwallets", "alice.pem");
2902+
const alice = await Account.newFromPem(filePath);
2903+
2904+
// fetch the nonce of the network
2905+
alice.nonce = await entrypoint.recallAccountNonce(alice.address);
2906+
2907+
const transaction = await controller.createTransactionForVoting(alice, alice.getNonceThenIncrement(), {
2908+
proposalNonce: 1,
2909+
vote: Vote.YES,
2910+
});
2911+
2912+
// sending the transaction
2913+
const txHash = await entrypoint.sendTransaction(transaction);
2914+
2915+
// wait for transaction completion, extract proposal's details
2916+
const outcome = await controller.awaitCompletedVote(txHash);
2917+
const proposalNonce = outcome[0].proposalNonce;
2918+
const vote = outcome[0].vote;
2919+
const voteTotalStake = outcome[0].totalStake;
2920+
const voteVotingPower = outcome[0].votingPower;
2921+
}
2922+
```
2923+
2924+
#### Vote for a proposal using the factory
2925+
```js
2926+
{
2927+
// create the entrypoint and the governance factory
2928+
const entrypoint = new DevnetEntrypoint();
2929+
const factory = entrypoint.createGovernanceTransactionsFactory();
2930+
2931+
const filePath = path.join("../src", "testdata", "testwallets", "alice.pem");
2932+
const alice = await Account.newFromPem(filePath);
2933+
2934+
const transaction = factory.createTransactionForVoting(alice.address, {
2935+
proposalNonce: 1,
2936+
vote: Vote.YES,
2937+
});
2938+
// fetch the nonce of the network
2939+
alice.nonce = await entrypoint.recallAccountNonce(alice.address);
2940+
2941+
// set the nonce
2942+
transaction.nonce = alice.getNonceThenIncrement();
2943+
2944+
// sign the transaction
2945+
transaction.signature = await alice.signTransaction(transaction);
2946+
2947+
// sending the transaction
2948+
const txHash = await entrypoint.sendTransaction(transaction);
2949+
2950+
// wait for transaction completion, extract proposal's details
2951+
const transactionOnNetwork = await entrypoint.awaitCompletedTransaction(txHash);
2952+
const parser = new GovernanceTransactionsOutcomeParser({});
2953+
const outcome = parser.parseVote(transactionOnNetwork);
2954+
const proposalNonce = outcome[0].proposalNonce;
2955+
const vote = outcome[0].vote;
2956+
const voteTotalStake = outcome[0].totalStake;
2957+
const voteVotingPower = outcome[0].votingPower;
2958+
}
2959+
```
2960+
2961+
#### Querying the governance contract
2962+
Unlike creating transactions, querying the contract is only possible using the controller. Let's query the contract to get more details about a proposal.
2963+
2964+
```js
2965+
{
2966+
// create the entrypoint and the governance controller
2967+
const entrypoint = new DevnetEntrypoint();
2968+
const controller = entrypoint.createGovernanceController();
2969+
2970+
const proposalInfo = await controller.getProposal(1);
2971+
console.log({ proposalInfo });
2972+
}
2973+
```
2974+
26292975
## Addresses
26302976

26312977
Create an `Address` object from a bech32-encoded string:

0 commit comments

Comments
 (0)