Skip to content

Commit 4e33c4f

Browse files
authored
Merge pull request #621 from multiversx/feat/next
Multisig release
2 parents 70045e8 + 43ae624 commit 4e33c4f

29 files changed

+5168
-1439
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@multiversx/sdk-core",
3-
"version": "14.1.2",
3+
"version": "14.2.0",
44
"description": "MultiversX SDK for JavaScript and TypeScript",
55
"author": "MultiversX",
66
"homepage": "https://multiversx.com",

src/abi/smartContract.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,7 @@ import {
1919
} from "./interface";
2020
import { NativeSerializer } from "./nativeSerializer";
2121
import { Query } from "./query";
22-
import { EndpointDefinition, TypedValue } from "./typesystem";
23-
24-
interface IAbi {
25-
constructorDefinition: EndpointDefinition;
26-
27-
getEndpoints(): EndpointDefinition[];
28-
getEndpoint(name: string | ContractFunction): EndpointDefinition;
29-
}
22+
import { Abi, EndpointDefinition, TypedValue } from "./typesystem";
3023

3124
/**
3225
* * @deprecated component. Use "SmartContractTransactionsFactory" or "SmartContractController", instead.
@@ -35,7 +28,7 @@ interface IAbi {
3528
*/
3629
export class SmartContract implements ISmartContract {
3730
private address: Address = Address.empty();
38-
private abi?: IAbi;
31+
private abi?: Abi;
3932

4033
/**
4134
* This object contains a function for each endpoint defined by the contract.
@@ -55,7 +48,7 @@ export class SmartContract implements ISmartContract {
5548
/**
5649
* Create a SmartContract object by providing its address on the Network.
5750
*/
58-
constructor(options: { address?: Address; abi?: IAbi } = {}) {
51+
constructor(options: { address?: Address; abi?: Abi } = {}) {
5952
this.address = options.address || Address.empty();
6053
this.abi = options.abi;
6154

@@ -105,13 +98,16 @@ export class SmartContract implements ISmartContract {
10598
return this.address;
10699
}
107100

108-
private getAbi(): IAbi {
101+
private getAbi(): Abi {
109102
guardValueIsSet("abi", this.abi);
110103
return this.abi!;
111104
}
112105

113106
getEndpoint(name: string | ContractFunction): EndpointDefinition {
114-
return this.getAbi().getEndpoint(name);
107+
if (typeof name === "string") {
108+
return this.getAbi().getEndpoint(name);
109+
}
110+
return this.getAbi().getEndpoint(name.name);
115111
}
116112

117113
/**

src/core/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const VM_TYPE_WASM_VM = new Uint8Array([0x05, 0x00]);
1414
export const CONTRACT_DEPLOY_ADDRESS_HEX = "0000000000000000000000000000000000000000000000000000000000000000";
1515
export const DELEGATION_MANAGER_SC_ADDRESS_HEX = "000000000000000000010000000000000000000000000000000000000004ffff";
1616
export const ESDT_CONTRACT_ADDRESS_HEX = "000000000000000000010000000000000000000000000000000000000002ffff";
17+
export const GOVERNANCE_CONTRACT_ADDRESS_HEX = "000000000000000000010000000000000000000000000000000000000003ffff";
1718

1819
export const DEFAULT_MESSAGE_VERSION = 1;
1920
export const MESSAGE_PREFIX = "\x17Elrond Signed Message:\n";

src/core/transactionsFactoryConfig.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ export class TransactionsFactoryConfig {
4444
gasLimitNftChangeToDynamic: bigint;
4545
gasLimitUpdateTokenId: bigint;
4646
gasLimitRegisterDynamic: bigint;
47+
gasLimitForProposal: bigint;
48+
gasLimitForVote: bigint;
49+
gasLimitForClosingProposal: bigint;
50+
gasLimitForClearProposals: bigint;
51+
gasLimitForChangeConfig: bigint;
52+
gasLimitForClaimAccumulatedFees: bigint;
4753

4854
constructor(options: { chainID: string }) {
4955
// General-purpose configuration
@@ -100,5 +106,13 @@ export class TransactionsFactoryConfig {
100106
// Configuration for smart contract operations
101107
this.gasLimitClaimDeveloperRewards = 6000000n;
102108
this.gasLimitChangeOwnerAddress = 6000000n;
109+
110+
// Configuration for governance operations
111+
this.gasLimitForProposal = 50_000_000n;
112+
this.gasLimitForVote = 5_000_000n;
113+
this.gasLimitForClosingProposal = 50_000_000n;
114+
this.gasLimitForClearProposals = 50_000_000n;
115+
this.gasLimitForChangeConfig = 50_000_000n;
116+
this.gasLimitForClaimAccumulatedFees = 1_000_000n;
103117
}
104118
}

src/entrypoints/entrypoints.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
TransactionWatcher,
1313
} from "../core";
1414
import { DelegationController, DelegationTransactionsFactory } from "../delegation";
15+
import { MultisigTransactionsFactory } from "../multisig";
16+
import { MultisigController } from "../multisig/multisigController";
1517
import { ApiNetworkProvider, ProxyNetworkProvider } from "../networkProviders";
1618
import { INetworkProvider } from "../networkProviders/interface";
1719
import { SmartContractTransactionsFactory } from "../smartContracts";
@@ -180,6 +182,17 @@ export class NetworkEntrypoint {
180182
config: new TransactionsFactoryConfig({ chainID: this.chainId }),
181183
});
182184
}
185+
186+
createMultisigController(abi: Abi): MultisigController {
187+
return new MultisigController({ chainID: this.chainId, networkProvider: this.networkProvider, abi: abi });
188+
}
189+
190+
createMultisigTransactionsFactory(abi: Abi): MultisigTransactionsFactory {
191+
return new MultisigTransactionsFactory({
192+
config: new TransactionsFactoryConfig({ chainID: this.chainId }),
193+
abi: abi,
194+
});
195+
}
183196
}
184197

185198
export class TestnetEntrypoint extends NetworkEntrypoint {
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import { assert } from "chai";
2+
import { Account } from "../accounts";
3+
import { SmartContractQueryResponse } from "../core";
4+
import { Address } from "../core/address";
5+
import { ProxyNetworkProvider } from "../networkProviders";
6+
import { b64TopicsToBytes, MockNetworkProvider } from "../testutils";
7+
import { KeyPair, UserSecretKey } from "../wallet";
8+
import { GovernanceController } from "./governanceController";
9+
import { Vote } from "./resources";
10+
11+
describe("test governance controller", function () {
12+
const chainID = "D";
13+
const controller = new GovernanceController({
14+
chainID: chainID,
15+
networkProvider: new ProxyNetworkProvider("https://devnet-gateway.multiversx.com"),
16+
});
17+
18+
const commitHash = "1db734c0315f9ec422b88f679ccfe3e0197b9d67";
19+
const governanceAddress = "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrlllsrujgla";
20+
21+
const aliceBech32 = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th";
22+
const secretKey = UserSecretKey.fromString("413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9");
23+
const keypair = new KeyPair(secretKey);
24+
const alice = Account.newFromKeypair(keypair);
25+
26+
it("should create transaction for creating new proposal", async function () {
27+
const expectedData = `proposal@${Buffer.from(commitHash).toString("hex")}@0a@0f`;
28+
29+
const transaction = await controller.createTransactionForNewProposal(alice, alice.getNonceThenIncrement(), {
30+
commitHash: commitHash,
31+
startVoteEpoch: 10,
32+
endVoteEpoch: 15,
33+
nativeTokenAmount: 1000_000000000000000000n,
34+
});
35+
36+
assert.equal(transaction.sender.toBech32(), aliceBech32);
37+
assert.equal(transaction.receiver.toBech32(), governanceAddress);
38+
assert.equal(transaction.value, 1000_000000000000000000n);
39+
assert.equal(transaction.chainID, chainID);
40+
assert.equal(transaction.gasLimit, 50_192_500n);
41+
assert.equal(transaction.data.toString(), expectedData);
42+
});
43+
44+
it("should create transaction for voting", async function () {
45+
const transaction = await controller.createTransactionForVoting(alice, alice.getNonceThenIncrement(), {
46+
proposalNonce: 1,
47+
vote: Vote.YES,
48+
});
49+
50+
assert.equal(transaction.sender.toBech32(), aliceBech32);
51+
assert.equal(transaction.receiver.toBech32(), governanceAddress);
52+
assert.equal(transaction.value, 0n);
53+
assert.equal(transaction.chainID, chainID);
54+
assert.equal(transaction.gasLimit, 5_171_000n);
55+
assert.equal(transaction.data.toString(), "vote@01@796573");
56+
});
57+
58+
it("should create transaction for closing proposal", async function () {
59+
const transaction = await controller.createTransactionForClosingProposal(alice, alice.getNonceThenIncrement(), {
60+
proposalNonce: 1,
61+
});
62+
63+
assert.equal(transaction.sender.toBech32(), aliceBech32);
64+
assert.equal(transaction.receiver.toBech32(), governanceAddress);
65+
assert.equal(transaction.value, 0n);
66+
assert.equal(transaction.chainID, chainID);
67+
assert.equal(transaction.gasLimit, 50_074_000n);
68+
assert.equal(transaction.data.toString(), "closeProposal@01");
69+
});
70+
71+
it("should create transaction for clearing ended proposals", async function () {
72+
const expectedData =
73+
"clearEndedProposals@0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8";
74+
75+
const transaction = await controller.createTransactionForClearingEndedProposals(
76+
alice,
77+
alice.getNonceThenIncrement(),
78+
{
79+
proposers: [
80+
alice.address,
81+
Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"),
82+
],
83+
},
84+
);
85+
86+
assert.equal(transaction.sender.toBech32(), aliceBech32);
87+
assert.equal(transaction.receiver.toBech32(), governanceAddress);
88+
assert.equal(transaction.value, 0n);
89+
assert.equal(transaction.chainID, chainID);
90+
assert.equal(transaction.gasLimit, 150_273_500n);
91+
assert.equal(transaction.data.toString(), expectedData);
92+
});
93+
94+
it("should create transaction for claiming accumulated fees", async function () {
95+
const transaction = await controller.createTransactionForClaimingAccumulatedFees(
96+
alice,
97+
alice.getNonceThenIncrement(),
98+
{},
99+
);
100+
101+
assert.equal(transaction.sender.toBech32(), aliceBech32);
102+
assert.equal(transaction.receiver.toBech32(), governanceAddress);
103+
assert.equal(transaction.value, 0n);
104+
assert.equal(transaction.chainID, chainID);
105+
assert.equal(transaction.gasLimit, 1_080_000n);
106+
assert.equal(transaction.data.toString(), "claimAccumulatedFees");
107+
});
108+
109+
it("should create transaction for changing config", async function () {
110+
const expectedData =
111+
"changeConfig@31303030303030303030303030303030303030303030@3130303030303030303030303030303030303030@35303030@33303030@36303030";
112+
113+
const transaction = await controller.createTransactionForChangingConfig(alice, alice.getNonceThenIncrement(), {
114+
proposalFee: 1000000000000000000000n,
115+
lastProposalFee: 10000000000000000000n,
116+
minQuorum: 5000,
117+
minVetoThreshold: 3000,
118+
minPassThreshold: 6000,
119+
});
120+
121+
assert.equal(transaction.sender.toBech32(), aliceBech32);
122+
assert.equal(transaction.receiver.toBech32(), governanceAddress);
123+
assert.equal(transaction.value, 0n);
124+
assert.equal(transaction.chainID, chainID);
125+
assert.equal(transaction.gasLimit, 50_237_500n);
126+
assert.equal(transaction.data.toString(), expectedData);
127+
});
128+
129+
it("should get voting power", async function () {
130+
const provider = new MockNetworkProvider();
131+
const controller = new GovernanceController({
132+
chainID: chainID,
133+
networkProvider: provider,
134+
});
135+
136+
provider.mockQueryContractOnFunction(
137+
"viewVotingPower",
138+
new SmartContractQueryResponse({
139+
returnDataParts: [Buffer.from("878678326eac900000", "hex")],
140+
returnCode: "ok",
141+
returnMessage: "",
142+
function: "viewVotingPower",
143+
}),
144+
);
145+
146+
const votingPower = await controller.getVotingPower(alice.address);
147+
assert.equal(votingPower, 2500_000000000000000000n);
148+
});
149+
150+
it("should get config", async function () {
151+
const provider = new MockNetworkProvider();
152+
const controller = new GovernanceController({
153+
chainID: chainID,
154+
networkProvider: provider,
155+
});
156+
157+
provider.mockQueryContractOnFunction(
158+
"viewConfig",
159+
new SmartContractQueryResponse({
160+
returnDataParts: [
161+
Buffer.from("1000000000000000000000"),
162+
Buffer.from("0.2000"),
163+
Buffer.from("0.5000"),
164+
Buffer.from("0.3300"),
165+
Buffer.from("1"),
166+
],
167+
returnCode: "ok",
168+
returnMessage: "",
169+
function: "viewConfig",
170+
}),
171+
);
172+
173+
const config = await controller.getConfig();
174+
assert.equal(config.proposalFee, 1000_000000000000000000n);
175+
assert.equal(config.minQuorum, 0.2);
176+
assert.equal(config.minPassThreshold, 0.5);
177+
assert.equal(config.minVetoThreshold, 0.33);
178+
assert.equal(config.lastProposalNonce, 1);
179+
});
180+
181+
it("should get proposal", async function () {
182+
const provider = new MockNetworkProvider();
183+
const controller = new GovernanceController({
184+
chainID: chainID,
185+
networkProvider: provider,
186+
});
187+
188+
provider.mockQueryContractOnFunction(
189+
"viewProposal",
190+
new SmartContractQueryResponse({
191+
returnDataParts: b64TopicsToBytes([
192+
"NjXJrcXeoAAA",
193+
"MWRiNzM0YzAzMTVmOWVjNDIyYjg4ZjY3OWNjZmUzZTAxOTdiOWQ2Nw==",
194+
"AQ==",
195+
"ATlHLv9ohncamC8wg9pdQh8kwpGB5jiIIo3IHKYNaeE=",
196+
"NQ==",
197+
"Nw==",
198+
"",
199+
"",
200+
"",
201+
"",
202+
"",
203+
"ZmFsc2U=",
204+
"ZmFsc2U=",
205+
]),
206+
returnCode: "ok",
207+
returnMessage: "",
208+
function: "viewProposal",
209+
}),
210+
);
211+
212+
const proposal = await controller.getProposal(1);
213+
assert.equal(proposal.cost, 1000_000000000000000000n);
214+
assert.equal(proposal.commitHash, "1db734c0315f9ec422b88f679ccfe3e0197b9d67");
215+
assert.equal(proposal.nonce, 1);
216+
assert.equal(proposal.issuer.toBech32(), aliceBech32);
217+
assert.equal(proposal.startVoteEpoch, 53);
218+
assert.equal(proposal.endVoteEpoch, 55);
219+
assert.equal(proposal.quorumStake, 0n);
220+
assert.equal(proposal.numYesVotes, 0n);
221+
assert.equal(proposal.numNoVotes, 0n);
222+
assert.equal(proposal.numVetoVotes, 0n);
223+
assert.equal(proposal.numAbstainVotes, 0n);
224+
assert.equal(proposal.isClosed, false);
225+
assert.equal(proposal.isPassed, false);
226+
});
227+
});

0 commit comments

Comments
 (0)