From 59fe5397314bf5059d374b8a988f281666020f71 Mon Sep 17 00:00:00 2001 From: danielailie Date: Mon, 14 Apr 2025 12:24:15 +0300 Subject: [PATCH 01/14] Add multisig transaction factory --- .../multisigTransactionsFactory.spec.ts | 530 ++++ src/multisig/multisigTransactionsFactory.ts | 510 ++++ src/multisig/resources.ts | 433 +++ .../smartContractTransactionsFactory.ts | 6 +- src/testdata/multisig-full.abi.json | 2533 ++++++++--------- 5 files changed, 2730 insertions(+), 1282 deletions(-) create mode 100644 src/multisig/multisigTransactionsFactory.spec.ts create mode 100644 src/multisig/multisigTransactionsFactory.ts create mode 100644 src/multisig/resources.ts diff --git a/src/multisig/multisigTransactionsFactory.spec.ts b/src/multisig/multisigTransactionsFactory.spec.ts new file mode 100644 index 000000000..97bc2900a --- /dev/null +++ b/src/multisig/multisigTransactionsFactory.spec.ts @@ -0,0 +1,530 @@ +import { assert } from "chai"; +import { Abi, Code } from "../abi"; +import { CodeMetadata, Token, TokenTransfer } from "../core"; +import { Address } from "../core/address"; +import { Transaction } from "../core/transaction"; +import { TransactionsFactoryConfig } from "../core/transactionsFactoryConfig"; +import { loadAbiRegistry, loadContractCode } from "../testutils"; +import { MultisigTransactionsFactory } from "./multisigTransactionsFactory"; + +describe("test multisig transactions factory", function () { + const config = new TransactionsFactoryConfig({ + chainID: "D", + }); + + let bytecode: Code; + let abi: Abi; + let adderAbi: Abi; + let factory: MultisigTransactionsFactory; + before(async function () { + bytecode = await loadContractCode("src/testdata/multisig-full.wasm"); + abi = await loadAbiRegistry("src/testdata/multisig-full.abi.json"); + adderAbi = await loadAbiRegistry("src/testdata/adder.abi.json"); + + factory = new MultisigTransactionsFactory({ + config: config, + abi: abi, + }); + }); + + it("should create transaction for deploy multisig contract", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const boardMemberAddress = Address.newFromBech32( + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + ); + const proposerAddress = Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"); + + const board = [boardMemberAddress, proposerAddress]; + const amount = 1000000000000000000n; // 1 EGLD + + const transaction = factory.createTransactionForMultisigDeploy(senderAddress, { + bytecode: bytecode.valueOf(), + gasLimit: 5000000n, + quorum: 2, + board, + amount, + }); + const res = Buffer.from(transaction.data).toString().split("@"); + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu"); + assert.equal(transaction.value, amount); + assert.equal(transaction.chainID, config.chainID); + assert.deepEqual( + Buffer.from(transaction.data), + Buffer.from( + `${bytecode}@0500@0504@02@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@b2a11555ce521e4944e09ab17549d85b487dcd26c84b5017a39e31a3670889ba`, + ), + ); + }); + + it("should create transaction for propose add board member", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const boardMemberAddress = Address.newFromBech32( + "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", + ); + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const transaction = factory.createTransactionForProposeAddBoardMember(senderAddress, { + multisigContract: multisigContractAddress, + gasLimit: 5000000n, + boardMemberAddress: boardMemberAddress, + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.equal(transaction.chainID, config.chainID); + assert.deepEqual( + transaction.data.toString(), + "proposeAddBoardMember@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8", + ); + }); + + it("should create transaction for propose add proposer", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const proposerAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const transaction = factory.createTransactionForProposeAddProposer(senderAddress, { + multisigContract: multisigContractAddress, + gasLimit: 5000000n, + proposerAddress: proposerAddress, + }); + + 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(), + "proposeAddProposer@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8", + ); + }); + + it("should create transaction for propose remove user", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const userAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const transaction = factory.createTransactionForProposeRemoveUser(senderAddress, { + multisigContract: multisigContractAddress, + gasLimit: 5000000n, + userAddress: userAddress, + }); + + 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(), + "proposeRemoveUser@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8", + ); + }); + + it("should create transaction for propose change quorum", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const transaction = factory.createTransactionForProposeChangeQuorum(senderAddress, { + multisigContract: multisigContractAddress, + gasLimit: 5000000n, + newQuorum: 3, + }); + + 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(), "proposeChangeQuorum@03"); + }); + + it("should create transaction for propose transfer execute", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const destinationContract = Address.newFromBech32( + "erd1qqqqqqqqqqqqqpgq0rffvv4vk9vesqplv9ws55fxzdfaspqa8cfszy2hms", + ); + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqpgq6kurkz43xq8t35kx9p8rvyz5kpxe9g7qd8ssefqjw8", + ); + const amount = 1000000000000000000n; // 1 EGLD + const transaction = factory.createTransactionForProposeTransferExecute(senderAddress, { + multisigContract: multisigContractAddress, + gasLimit: 5000000n, + egldAmount: amount, + to: destinationContract, + functionName: "add", + functionArguments: [7], + abi: adderAbi, + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.equal(transaction.chainID, config.chainID); + assert.deepEqual( + transaction.data.toString(), + "proposeTransferExecute@0000000000000000050078d29632acb15998003f615d0a51261353d8041d3e13@0de0b6b3a7640000@0100000000004c4b40@616464@07", + ); + }); + + it("should create transaction for propose transfer execute ESDT", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const destinationContract = Address.newFromBech32( + "erd1qqqqqqqqqqqqqpgqfxlljcaalgl2qfcnxcsftheju0ts36kvl3ts3qkewe", + ); + + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const token = new Token({ + identifier: "ALICE-5627f1", + }); + const tokenTransfer = new TokenTransfer({ token: token, amount: 10n }); + + const transaction = factory.createTransactionForProposeTransferExecuteEsdt(senderAddress, { + multisigContract: multisigContractAddress, + gasLimit: 5000000n, + to: destinationContract, + tokens: [tokenTransfer], + functionName: "distribute", + functionArguments: [], + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.equal(transaction.chainID, config.chainID); + assert.deepEqual( + transaction.data.toString(), + "proposeTransferExecuteEsdt@0000000000000000050049bff963bdfa3ea02713362095df32e3d708eaccfc57@0000000c414c4943452d3536323766310000000000000000000000010a@0100000000004c4b40@3634363937333734373236393632373537343635", + ); + }); + + it("should create transaction for propose async call", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const destinationContract = Address.newFromBech32( + "erd1qqqqqqqqqqqqqpgq0rffvv4vk9vesqplv9ws55fxzdfaspqa8cfszy2hms", + ); + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqpgq6kurkz43xq8t35kx9p8rvyz5kpxe9g7qd8ssefqjw8", + ); + const transaction = factory.createTransactionForProposeAsyncCall(senderAddress, { + multisigContract: multisigContractAddress, + gasLimit: 5000000n, + nativeTransferAmount: 0n, + to: destinationContract, + functionName: "add", + functionArguments: [7], + tokenTransfers: [], + abi: adderAbi, + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.equal(transaction.chainID, config.chainID); + assert.equal( + transaction.data.toString(), + "proposeAsyncCall@0000000000000000050078d29632acb15998003f615d0a51261353d8041d3e13@@4c4b40@616464@07", + ); + }); + + it("should create transaction for deposit the expected amount of egld", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + + const transaction = factory.createTransactionForDeposit(senderAddress, { + multisigContract: multisigContractAddress, + gasLimit: 5000000n, + egldAmount: 1n, + tokenTransfers: [], + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.equal(transaction.chainID, config.chainID); + assert.equal(transaction.value, 1n); + assert.deepEqual(transaction.data.toString(), "deposit"); + }); + + it("should create transaction for deposit esdt token", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const token = new Token({ + identifier: "ALICE-5627f1", + }); + const tokenTransfer = new TokenTransfer({ token: token, amount: 100n }); + + const transaction = factory.createTransactionForDeposit(senderAddress, { + multisigContract: multisigContractAddress, + gasLimit: 5000000n, + egldAmount: 0n, + tokenTransfers: [tokenTransfer], + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.equal(transaction.chainID, config.chainID); + assert.equal(transaction.value, 0n); + assert.deepEqual(transaction.data.toString(), "ESDTTransfer@414c4943452d353632376631@64@6465706f736974"); + }); + + it("should create transaction for propose SC deploy from source", function () { + const amount = BigInt(50000000000000000); // 0.05 EGLD + const metadata = new CodeMetadata(true, true, false); + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const sourceContract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqsuxsgykwm6r3s5apct2g5a2rcpe7kw0ed8ssf6h9f6"); + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqpgq0cjuum0t436gmp446wf3yz43avp2gm2czeus8mctaf", + ); + + const transaction = factory.createTransactionForProposeSCDeployFromSource(senderAddress, { + multisigContract: multisigContractAddress, + gasLimit: 5000000n, + amount: amount, + source: sourceContract, + codeMetadata: metadata, + arguments: ["7"], + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.equal(transaction.chainID, config.chainID); + assert.deepEqual( + transaction.data.toString(), + "proposeSCDeployFromSource@b1a2bc2ec50000@00000000000000000500870d0412cede871853a1c2d48a7543c073eb39f969e1@0500@7", + ); + }); + + it("should create transaction for propose SC upgrade from source", function () { + const amount = BigInt(50000000000000000); // 0.05 EGLD + const metadata = new CodeMetadata(true, true, false); + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const sourceContract = Address.newFromBech32("erd1qqqqqqqqqqqqqpgqd273cw3hjndqzcpts4dvq0ncy8nx8rkgzeusnefvaq"); + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqpgq0cjuum0t436gmp446wf3yz43avp2gm2czeus8mctaf", + ); + + const transaction = factory.createTransactionForProposeSCUpgradeFromSource(senderAddress, { + multisigContract: multisigContractAddress, + gasLimit: 5000000n, + scAddress: multisigContractAddress, + amount: amount, + source: sourceContract, + codeMetadata: metadata, + arguments: [], + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.equal(transaction.chainID, config.chainID); + assert.deepEqual( + transaction.data.toString(), + "proposeSCUpgradeFromSource@000000000000000005007e25ce6debac748d86b5d393120ab1eb02a46d581679@b1a2bc2ec50000@000000000000000005006abd1c3a3794da01602b855ac03e7821e6638ec81679@0500", + ); + }); + + it("should create transaction for sign action", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const transaction = factory.createTransactionForSignAction(senderAddress, { + multisigContract: multisigContractAddress, + actionId: 42, + gasLimit: 5000000n, + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + + assert.equal(transaction.chainID, config.chainID); + assert.deepEqual(transaction.data.toString(), "sign@2a"); + }); + + it("should create transaction for sign batch", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const transaction = factory.createTransactionForSignBatch(senderAddress, { + multisigContract: multisigContractAddress, + groupId: 5, + gasLimit: 5000000n, + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.equal(transaction.chainID, config.chainID); + assert.deepEqual(transaction.data.toString(), "signBatch@05"); + }); + + it("should create transaction for sign and perform", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const transaction = factory.createTransactionForSignAndPerform(senderAddress, { + multisigContract: multisigContractAddress, + actionId: 42, + gasLimit: 5000000n, + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + + assert.equal(transaction.chainID, config.chainID); + assert.deepEqual(transaction.data.toString(), "signAndPerform@2a"); + }); + + it("should create transaction for unsign", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const transaction = factory.createTransactionForUnsign(senderAddress, { + multisigContract: multisigContractAddress, + actionId: 42, + gasLimit: 5000000n, + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.deepEqual(transaction.data.toString(), "unsign@2a"); + }); + + it("should create transaction for unsign for outdated board members", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const transaction = factory.createTransactionForUnsignForOutdatedBoardMembers(senderAddress, { + multisigContract: multisigContractAddress, + actionId: 42, + outdatedBoardMembers: [1, 3, 5], + gasLimit: 5000000n, + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.deepEqual(transaction.data.toString(), "unsignForOutdatedBoardMembers@2a@01@03@05"); + }); + + it("should create transaction for perform action", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const transaction = factory.createTransactionForPerformAction(senderAddress, { + multisigContract: multisigContractAddress, + actionId: 42, + gasLimit: 5000000n, + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.deepEqual(transaction.data.toString(), "performAction@2a"); + }); + + it("should create transaction for perform batch", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const transaction = factory.createTransactionForPerformBatch(senderAddress, { + multisigContract: multisigContractAddress, + groupId: 5, + gasLimit: 5000000n, + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.deepEqual(transaction.data.toString(), "performBatch@05"); + }); + + it("should create transaction for discard action", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const transaction = factory.createTransactionForDiscardAction(senderAddress, { + multisigContract: multisigContractAddress, + actionId: 322, + gasLimit: 5000000n, + }); + + assert.instanceOf(transaction, Transaction); + assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); + assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); + assert.isAbove(transaction.data.length, 0); + assert.equal(transaction.chainID, config.chainID); + assert.deepEqual(transaction.data.toString(), "discardAction@0142"); + }); + + it("should create transaction for discard batch", function () { + const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + + const multisigContractAddress = Address.newFromBech32( + "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", + ); + const transaction = factory.createTransactionForDiscardBatch(senderAddress, { + multisigContract: multisigContractAddress, + gasLimit: 5000000n, + actionIds: [24, 25], + }); + + 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(), "discardBatch@18@19"); + }); +}); diff --git a/src/multisig/multisigTransactionsFactory.ts b/src/multisig/multisigTransactionsFactory.ts new file mode 100644 index 000000000..aa5ba209a --- /dev/null +++ b/src/multisig/multisigTransactionsFactory.ts @@ -0,0 +1,510 @@ +import { + AddressValue, + ArgSerializer, + BigUIntValue, + ContractFunction, + EndpointDefinition, + EndpointModifiers, + NativeSerializer, + OptionType, + OptionValue, + TokenIdentifierValue, + U32Value, + U64Type, + U64Value, + VariadicValue, +} from "../abi"; +import { TokenComputer, TransactionsFactoryConfig } from "../core"; +import { Address } from "../core/address"; +import { Transaction } from "../core/transaction"; +import { TransactionBuilder } from "../core/transactionBuilder"; +import { SmartContractTransactionsFactory } from "../smartContracts"; +import * as resources from "./resources"; + +interface IAbi { + constructorDefinition: EndpointDefinition; + upgradeConstructorDefinition?: EndpointDefinition; + + getEndpoint(name: string | ContractFunction): EndpointDefinition; +} +/** + * Use this class to create multisig related transactions like creating a new multisig contract, + * proposing actions, signing actions, and performing actions. + */ +export class MultisigTransactionsFactory extends SmartContractTransactionsFactory { + private readonly argSerializer: ArgSerializer; + + constructor(options: { config: TransactionsFactoryConfig; abi?: IAbi }) { + super(options); + this.argSerializer = new ArgSerializer(); + } + + /** + * Creates a transaction to deploy a new multisig contract + */ + createTransactionForMultisigDeploy(sender: Address, options: resources.DeployMultisigContractInput): Transaction { + const nativeTransferAmount = options.amount ?? 0n; + const boardAddresses: AddressValue[] = options.board.map((addr) => new AddressValue(addr)); + const args = [new U32Value(options.quorum), VariadicValue.fromItems(...boardAddresses)]; + + return this.createTransactionForDeploy(sender, { + bytecode: options.bytecode, + gasLimit: options.gasLimit, + nativeTransferAmount, + isUpgradeable: options.isUpgradeable, + isReadable: options.isReadable, + isPayable: options.isPayable, + isPayableBySmartContract: options.isPayableBySmartContract, + arguments: args, + }); + } + + /** + * Proposes adding a new board member + */ + createTransactionForProposeAddBoardMember( + sender: Address, + options: resources.ProposeAddBoardMemberInput, + ): Transaction { + const dataParts = [ + "proposeAddBoardMember", + this.argSerializer.valuesToStrings([new AddressValue(options.boardMemberAddress)])[0], + ]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Proposes adding a new proposer + */ + createTransactionForProposeAddProposer(sender: Address, options: resources.ProposeAddProposerInput): Transaction { + const dataParts = [ + "proposeAddProposer", + this.argSerializer.valuesToStrings([new AddressValue(options.proposerAddress)])[0], + ]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Proposes removing a user (board member or proposer) + */ + createTransactionForProposeRemoveUser(sender: Address, options: resources.ProposeRemoveUserInput): Transaction { + const dataParts = [ + "proposeRemoveUser", + this.argSerializer.valuesToStrings([new AddressValue(options.userAddress)])[0], + ]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Proposes changing the quorum (minimum signatures required) + */ + createTransactionForProposeChangeQuorum(sender: Address, options: resources.ProposeChangeQuorumInput): Transaction { + const dataParts = [ + "proposeChangeQuorum", + this.argSerializer.valuesToStrings([new U32Value(options.newQuorum)])[0], + ]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Proposes a transaction that will transfer EGLD and/or execute a function + */ + createTransactionForProposeTransferExecute( + sender: Address, + options: resources.ProposeTransferExecuteInput, + ): Transaction { + const gasOption = new U64Value(options.gasLimit); + const input = resources.ProposeTransferExecutInput.newFromTransferExecuteInput({ + multisig: options.multisigContract, + to: options.to, + tokenTransfers: [], + functionName: options.functionName, + arguments: options.functionArguments, + abi: options.abi, + }); + const dataParts = [ + "proposeTransferExecute", + this.argSerializer.valuesToStrings([new AddressValue(options.to)])[0], + this.argSerializer.valuesToStrings([new BigUIntValue(options.egldAmount)])[0], + this.argSerializer.valuesToStrings([new OptionValue(new OptionType(new U64Type()), gasOption)])[0], + ...input.functionCall, + ]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Proposes a transaction that will transfer EGLD and/or execute a function + */ + createTransactionForDeposit(sender: Address, options: resources.DepositExecuteInput): Transaction { + return this.createTransactionForExecute(sender, { + contract: options.multisigContract, + function: "deposit", + gasLimit: options.gasLimit ?? 0n, + arguments: [], + nativeTransferAmount: options.egldAmount, + tokenTransfers: options.tokenTransfers, + }); + } + + /** + * Proposes a transaction that will transfer ESDT tokens and/or execute a function + */ + createTransactionForProposeTransferExecuteEsdt( + sender: Address, + options: resources.ProposeTransferExecuteEsdtInput, + ): Transaction { + const input = resources.ProposeTransferExecutInput.newFromTransferExecuteInput({ + multisig: options.multisigContract, + to: options.to, + tokenTransfers: [], + functionName: options.functionName, + arguments: options.functionArguments, + abi: options.abi, + }); + const tokenComputer = new TokenComputer(); + const argsTyped = []; + for (const token of options.tokens) { + const identifier = tokenComputer.extractIdentifierFromExtendedIdentifier(token.token.identifier); + argsTyped.push({ + token_identifier: new TokenIdentifierValue( + tokenComputer.extractIdentifierFromExtendedIdentifier(token.token.identifier), + ), + token_nonce: new U64Value(token.token.nonce), + amount: new BigUIntValue(token.amount), + }); + } + const dataParts = [ + "proposeTransferExecuteEsdt", + ...this.argSerializer.valuesToStrings( + NativeSerializer.nativeToTypedValues( + [options.to, argsTyped, options.gasLimit, VariadicValue.fromItems(...input.functionCall)], + this.abi?.getEndpoint("proposeTransferExecuteEsdt") ?? + new EndpointDefinition("proposeTransferExecuteEsdt", [], [], new EndpointModifiers("", [])), + ), + ), + ]; + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Proposes an async call to another contract + */ + createTransactionForProposeAsyncCall(sender: Address, options: resources.ProposeAsyncCallInput): Transaction { + const input = resources.ProposeTransferExecutInput.newFromTransferExecuteInput({ + multisig: options.multisigContract, + to: options.to, + tokenTransfers: options.tokenTransfers, + functionName: options.functionName, + arguments: options.functionArguments, + abi: options.abi, + }); + let receiver = options.multisigContract; + const dataParts = [ + "proposeAsyncCall", + this.argSerializer.valuesToStrings([new AddressValue(options.to)])[0], + this.argSerializer.valuesToStrings([new BigUIntValue(options.nativeTransferAmount)])[0], + this.argSerializer.valuesToStrings([new BigUIntValue(options.gasLimit ?? 0n)])[0], + ...input.functionCall, + ]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: receiver, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: false, + amount: 0n, + }).build(); + } + + /** + * Proposes deploying a smart contract from source + */ + createTransactionForProposeSCDeployFromSource( + sender: Address, + options: resources.ProposeSCDeployFromSourceInput, + ): Transaction { + const dataParts = [ + "proposeSCDeployFromSource", + this.argSerializer.valuesToStrings([new BigUIntValue(options.amount)])[0], + this.argSerializer.valuesToStrings([new AddressValue(options.source)])[0], + options.codeMetadata.toString(), + ...options.arguments, + ]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Proposes upgrading a smart contract from source + */ + createTransactionForProposeSCUpgradeFromSource( + sender: Address, + options: resources.ProposeSCUpgradeFromSourceInput, + ): Transaction { + const dataParts = [ + "proposeSCUpgradeFromSource", + this.argSerializer.valuesToStrings([new AddressValue(options.scAddress)])[0], + this.argSerializer.valuesToStrings([new BigUIntValue(options.amount)])[0], + this.argSerializer.valuesToStrings([new AddressValue(options.source)])[0], + options.codeMetadata.toString(), + ...options.arguments, + ]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Signs an action (by a board member) + */ + createTransactionForSignAction(sender: Address, options: resources.ActionInput): Transaction { + const dataParts = ["sign", this.argSerializer.valuesToStrings([new U32Value(options.actionId)])[0]]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Signs all actions in a batch + */ + createTransactionForSignBatch(sender: Address, options: resources.GroupInput): Transaction { + const dataParts = ["signBatch", this.argSerializer.valuesToStrings([new U32Value(options.groupId)])[0]]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Signs and performs an action in one transaction + */ + createTransactionForSignAndPerform(sender: Address, options: resources.ActionInput): Transaction { + const dataParts = ["signAndPerform", this.argSerializer.valuesToStrings([new U32Value(options.actionId)])[0]]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Signs and performs all actions in a batch + */ + createTransactionForSignBatchAndPerform(sender: Address, options: resources.GroupInput): Transaction { + const dataParts = [ + "signBatchAndPerform", + this.argSerializer.valuesToStrings([new U32Value(options.groupId)])[0], + ]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Withdraws signature from an action + */ + createTransactionForUnsign(sender: Address, options: resources.ActionInput): Transaction { + const dataParts = ["unsign", this.argSerializer.valuesToStrings([new U32Value(options.actionId)])[0]]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Withdraws signatures from all actions in a batch + */ + createTransactionForUnsignBatch(sender: Address, options: resources.GroupInput): Transaction { + const dataParts = ["unsignBatch", this.argSerializer.valuesToStrings([new U32Value(options.groupId)])[0]]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Removes signatures from outdated board members + */ + createTransactionForUnsignForOutdatedBoardMembers( + sender: Address, + options: resources.UnsignForOutdatedBoardMembersInput, + ): Transaction { + const outdatedMembersArgs = options.outdatedBoardMembers.map( + (id) => this.argSerializer.valuesToStrings([new U32Value(id)])[0], + ); + + const dataParts = [ + "unsignForOutdatedBoardMembers", + this.argSerializer.valuesToStrings([new U32Value(options.actionId)])[0], + ...outdatedMembersArgs, + ]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Performs an action that has reached quorum + */ + createTransactionForPerformAction(sender: Address, options: resources.ActionInput): Transaction { + const dataParts = ["performAction", this.argSerializer.valuesToStrings([new U32Value(options.actionId)])[0]]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Performs all actions in a batch that have reached quorum + */ + createTransactionForPerformBatch(sender: Address, options: resources.GroupInput): Transaction { + const dataParts = ["performBatch", this.argSerializer.valuesToStrings([new U32Value(options.groupId)])[0]]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Discards an action that is no longer needed + */ + createTransactionForDiscardAction(sender: Address, options: resources.ActionInput): Transaction { + const dataParts = ["discardAction", this.argSerializer.valuesToStrings([new U32Value(options.actionId)])[0]]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } + + /** + * Discards all actions in the provided list + */ + createTransactionForDiscardBatch(sender: Address, options: resources.DiscardBatchInput): Transaction { + const actionIdsArgs = options.actionIds.map((id) => this.argSerializer.valuesToStrings([new U32Value(id)])[0]); + + const dataParts = ["discardBatch", ...actionIdsArgs]; + + return new TransactionBuilder({ + config: this.config, + sender: sender, + receiver: options.multisigContract, + dataParts: dataParts, + gasLimit: options.gasLimit, + addDataMovementGas: true, + }).build(); + } +} diff --git a/src/multisig/resources.ts b/src/multisig/resources.ts new file mode 100644 index 000000000..04c49a6d2 --- /dev/null +++ b/src/multisig/resources.ts @@ -0,0 +1,433 @@ +import { Abi, BytesValue } from "../abi"; +import { Token, TokenTransfer, TransactionsFactoryConfig } from "../core"; +import { Address } from "../core/address"; +import { CodeMetadata } from "../core/codeMetadata"; +import { ARGUMENTS_SEPARATOR } from "../core/constants"; +import { utf8ToHex } from "../core/utils.codec"; +import { SmartContractTransactionsFactory } from "../smartContracts"; + +export type DeployMultisigContractInput = { + quorum: number; + board: Address[]; + amount?: bigint; + bytecode: Uint8Array; + isUpgradeable?: boolean; + isReadable?: boolean; + isPayable?: boolean; + isPayableBySmartContract?: boolean; + gasLimit: bigint; +}; + +export type UpgradeMultisigContractInput = { + bytecode: Uint8Array; + isUpgradeable?: boolean; + isReadable?: boolean; + isPayable?: boolean; + isPayableBySmartContract?: boolean; + multisigContract: Address; + gasLimit: bigint; +}; +export type MultisigContractInput = { + multisigContract: Address; + gasLimit: bigint; +}; + +export type ProposeAddBoardMemberInput = MultisigContractInput & { + boardMemberAddress: Address; +}; + +export type ProposeAddProposerInput = MultisigContractInput & { + proposerAddress: Address; +}; + +export type ProposeRemoveUserInput = MultisigContractInput & { + userAddress: Address; +}; + +export type ProposeChangeQuorumInput = MultisigContractInput & { + newQuorum: number; +}; + +export type ProposeTransferExecuteInput = MultisigContractInput & { + to: Address; + egldAmount: bigint; + gasLimit?: bigint; + functionName: string; + functionArguments: any[]; + abi?: Abi; +}; + +export type DepositExecuteInput = MultisigContractInput & { + egldAmount: bigint; + gasLimit?: bigint; + tokenTransfers: TokenTransfer[]; +}; + +export class ProposeTransferExecuteEsdtInput { + multisigContract: Address; + to: Address; + tokens: any[]; + gasLimit: bigint; + functionName: string; + functionArguments: any[]; + abi?: Abi; + + constructor(options: ProposeTransferExecuteEsdtInput) { + this.multisigContract = options.multisigContract; + this.to = options.to; + this.tokens = options.tokens; + this.gasLimit = options.gasLimit; + this.functionName = options.functionName; + this.functionArguments = options.functionArguments; + this.abi = options.abi; + } +} + +export class ProposeTransferExecuteEsdtInputForContract { + multisigContract: Address; + to: Address; + gasLimit?: bigint; + functionCall: any[]; + tokens: EsdtTokenPayment[]; + + constructor(options: { + multisigContract: Address; + to: Address; + gasLimit?: bigint; + functionCall: any[]; + tokens: EsdtTokenPayment[]; + }) { + this.multisigContract = options.multisigContract; + this.to = options.to; + this.gasLimit = options.gasLimit; + this.functionCall = options.functionCall; + this.tokens = options.tokens; + } + + static newFromTransferExecuteInput(options: { + multisig: Address; + to: Address; + nativeTransferAmount: bigint; + tokenTransfers: TokenTransfer[]; + functionName: string; + arguments: any[]; + optGasLimit?: bigint; + abi?: Abi; + }): ProposeTransferExecuteEsdtInputForContract { + const transactionsFactory = new SmartContractTransactionsFactory({ + config: new TransactionsFactoryConfig({ chainID: "" }), + abi: options.abi, + }); + const transaction = transactionsFactory.createTransactionForExecute(Address.empty(), { + contract: Address.empty(), + function: options.functionName, + gasLimit: 0n, + arguments: options.arguments, + nativeTransferAmount: 0n, + tokenTransfers: options.tokenTransfers, + }); + + const tokens: EsdtTokenPayment[] = options.tokenTransfers.map((token) => { + return { token_identifier: token.token.identifier, token_nonce: token.token.nonce, amount: token.amount }; + }); + + const functionCallParts = Buffer.from(transaction.data).toString().split(ARGUMENTS_SEPARATOR); + const functionName = functionCallParts[0]; + const functionArguments = []; + for (let index = 1; index < functionCallParts.length; index++) { + const element = functionCallParts[index]; + functionArguments.push(element.valueOf()); + } + const functionCall = [new BytesValue(Buffer.from(utf8ToHex(functionName))), ...functionArguments]; + return new ProposeTransferExecuteEsdtInputForContract({ + multisigContract: options.multisig, + to: options.to, + functionCall: functionCall, + gasLimit: options.optGasLimit, + tokens: tokens, + }); + } +} + +export class ProposeTransferExecutInput { + multisigContract: Address; + to: Address; + gasLimit?: bigint; + functionCall: any[]; + + constructor(options: { multisigContract: Address; to: Address; gasLimit?: bigint; functionCall: any[] }) { + this.multisigContract = options.multisigContract; + this.to = options.to; + this.gasLimit = options.gasLimit; + this.functionCall = options.functionCall; + } + + static newFromTransferExecuteInput(options: { + multisig: Address; + to: Address; + tokenTransfers: TokenTransfer[]; + functionName: string; + arguments: any[]; + optGasLimit?: bigint; + abi?: Abi; + }): ProposeTransferExecutInput { + const transactionsFactory = new SmartContractTransactionsFactory({ + config: new TransactionsFactoryConfig({ chainID: "" }), + abi: options.abi, + }); + const transaction = transactionsFactory.createTransactionForExecute(Address.empty(), { + contract: Address.empty(), + function: options.functionName, + gasLimit: 0n, + arguments: options.arguments, + nativeTransferAmount: 0n, + tokenTransfers: options.tokenTransfers, + }); + const functionCallParts = Buffer.from(transaction.data).toString().split(ARGUMENTS_SEPARATOR); + const functionName = functionCallParts[0]; + const functionArguments = []; + for (let index = 1; index < functionCallParts.length; index++) { + const element = functionCallParts[index]; + functionArguments.push(element.valueOf()); + } + const functionCall = [new BytesValue(Buffer.from(utf8ToHex(functionName))), ...functionArguments]; + return new ProposeTransferExecutInput({ + multisigContract: options.multisig, + to: options.to, + functionCall: functionCall, + gasLimit: options.optGasLimit, + }); + } +} + +export class ProposeAsyncCallInput { + multisigContract: Address; + to: Address; + nativeTransferAmount: bigint; + tokenTransfers: TokenTransfer[]; + functionName: string; + functionArguments: any[]; + gasLimit: bigint; + abi?: Abi; + /** + * + */ + constructor(options: { + multisigContract: Address; + to: Address; + nativeTransferAmount: bigint; + tokenTransfers: TokenTransfer[]; + functionName: string; + functionArguments: any[]; + gasLimit: bigint; + abi?: Abi; + }) { + this.multisigContract = options.multisigContract; + this.to = options.to; + this.nativeTransferAmount = options.nativeTransferAmount; + this.tokenTransfers = options.tokenTransfers; + this.functionName = options.functionName; + this.functionArguments = options.functionArguments; + this.gasLimit = options.gasLimit; + this.abi = options.abi; + } +} + +export type ProposeSCDeployFromSourceInput = MultisigContractInput & { + amount: bigint; + source: Address; + codeMetadata: CodeMetadata; + arguments: string[]; +}; + +export type ProposeSCUpgradeFromSourceInput = MultisigContractInput & { + scAddress: Address; + amount: bigint; + source: Address; + codeMetadata: CodeMetadata; + arguments: string[]; +}; + +export type ActionInput = MultisigContractInput & { + actionId: number; +}; + +export type GroupInput = MultisigContractInput & { + groupId: number; +}; + +export type UnsignForOutdatedBoardMembersInput = ActionInput & { + outdatedBoardMembers: number[]; +}; + +export type DiscardBatchInput = MultisigContractInput & { + actionIds: number[]; +}; + +export enum UserRoleEnum { + None = "None", + Proposer = "Proposer", + BoardMember = "BoardMember", +} + +export enum MultisigActionEnum { + Nothing = "Nothing", + AddBoardMember = "AddBoardMember", + AddProposer = "AddProposer", + RemoveUser = "RemoveUser", + ChangeQuorum = "ChangeQuorum", + SendTransferExecuteEgld = "SendTransferExecuteEgld", + SendTransferExecuteEsdt = "SendTransferExecuteEsdt", + SendAsyncCall = "SendAsyncCall", + SCDeployFromSource = "SCDeployFromSource", + SCUpgradeFromSource = "SCUpgradeFromSource", +} + +export class MultisigAction { + public type: MultisigActionEnum = MultisigActionEnum.Nothing; +} +export class AddBoardMember extends MultisigAction { + public address: Address; + constructor(address: Address) { + super(); + this.type = MultisigActionEnum.AddBoardMember; + this.address = address; + } +} +export class AddProposer extends MultisigAction { + public address: Address; + constructor(address: Address) { + super(); + this.type = MultisigActionEnum.AddProposer; + this.address = address; + } +} +export class RemoveUser extends MultisigAction { + public type: MultisigActionEnum = MultisigActionEnum.RemoveUser; + public address: Address; + constructor(address: Address) { + super(); + this.type = MultisigActionEnum.RemoveUser; + this.address = address; + } +} + +export class ChangeQuorum extends MultisigAction { + public quorum: number; + constructor(quorum: number) { + super(); + this.type = MultisigActionEnum.ChangeQuorum; + this.quorum = quorum; + } +} + +export class SendTransferExecuteEgld extends MultisigAction { + receiver: Address; + amount: bigint; + optionalGasLimit?: number; + funcionName: string; + arguments: Uint8Array[]; + constructor(data: any) { + super(); + this.type = MultisigActionEnum.SendTransferExecuteEgld; + this.receiver = data.to; + this.amount = data.egld_amount; + this.optionalGasLimit = data.opt_gas_limit; + this.funcionName = data.endpoint_name.toString(); + this.arguments = data.arguments; + } +} +export class SendTransferExecuteEsdt extends MultisigAction { + receiver: Address; + tokens: TokenTransfer[]; + optionalGasLimit?: number; + funcionName: string; + arguments: Uint8Array[]; + constructor(data: any) { + super(); + this.type = MultisigActionEnum.SendTransferExecuteEsdt; + this.receiver = data.to; + this.tokens = data.tokens.map( + (token: { token_identifier: any; nonce: any; amount: any }) => + new TokenTransfer({ + token: new Token({ identifier: token.token_identifier, nonce: token.nonce }), + amount: token.amount, + }), + ); + this.optionalGasLimit = data.opt_gas_limit; + + this.funcionName = Buffer.from(data.endpoint_name.toString(), "hex").toString(); + this.arguments = data.arguments; + } +} + +export class SendAsyncCall extends MultisigAction { + receiver: Address; + amount: bigint; + optionalGasLimit?: number; + funcionName: string; + arguments: Uint8Array[]; + constructor(data: any) { + super(); + this.type = MultisigActionEnum.SendAsyncCall; + this.receiver = data.to; + this.amount = data.egld_amount; + this.optionalGasLimit = data.opt_gas_limit; + this.funcionName = data.endpoint_name.toString(); + this.arguments = data.arguments; + } +} + +export class SCDeployFromSource extends MultisigAction { + sourceContractAddress: Address; + amount: bigint; + codeMetadata: CodeMetadata; + arguments: Uint8Array[]; + constructor(data: any) { + super(); + this.type = MultisigActionEnum.SCDeployFromSource; + this.sourceContractAddress = data[1]; + this.amount = data[0]; + this.codeMetadata = data[2]; + this.arguments = data[3]; + } +} + +export class SCUpgradeFromSource extends MultisigAction { + sourceContractAddress: Address; + scAddress: Address; + amount: bigint; + codeMetadata: CodeMetadata; + arguments: Uint8Array[]; + constructor(data: any) { + super(); + this.type = MultisigActionEnum.SCUpgradeFromSource; + this.scAddress = data[0]; + this.amount = data[1]; + this.sourceContractAddress = data[2]; + this.codeMetadata = data[3]; + this.arguments = data[4]; + } +} + +export type CallActionData = { + receiver: Address; + amount: bigint; + optionalGasLimit?: number | null; + functionName: Uint8Array; + arguments: Uint8Array[]; +}; + +export type EsdtTokenPayment = { + token_identifier: any; + token_nonce: any; + amount: any; +}; + +export type EsdtTransferExecuteData = { + to: Address; + tokens: EsdtTokenPayment[]; + opt_gas_limit?: number | null; + endpoint_name: Uint8Array; + arguments: Uint8Array[]; +}; diff --git a/src/smartContracts/smartContractTransactionsFactory.ts b/src/smartContracts/smartContractTransactionsFactory.ts index 521a8f600..d66b0ecd6 100644 --- a/src/smartContracts/smartContractTransactionsFactory.ts +++ b/src/smartContracts/smartContractTransactionsFactory.ts @@ -30,8 +30,8 @@ interface IAbi { * Use this class to create transactions to deploy, call or upgrade a smart contract. */ export class SmartContractTransactionsFactory { - private readonly config: IConfig; - private readonly abi?: IAbi; + protected readonly config: IConfig; + protected readonly abi?: IAbi; private readonly tokenComputer: TokenComputer; private readonly dataArgsBuilder: TokenTransfersDataBuilder; private readonly contractDeployAddress: Address; @@ -195,7 +195,7 @@ export class SmartContractTransactionsFactory { }).build(); } - private argsToDataParts(args: any[], endpoint?: EndpointDefinition): string[] { + protected argsToDataParts(args: any[], endpoint?: EndpointDefinition): string[] { if (endpoint) { const typedArgs = NativeSerializer.nativeToTypedValues(args, endpoint); return new ArgSerializer().valuesToStrings(typedArgs); diff --git a/src/testdata/multisig-full.abi.json b/src/testdata/multisig-full.abi.json index 401de7cc0..6e9657dce 100644 --- a/src/testdata/multisig-full.abi.json +++ b/src/testdata/multisig-full.abi.json @@ -1,1304 +1,1279 @@ { - "buildInfo": { - "rustc": { - "version": "1.71.0-nightly", - "commitHash": "a2b1646c597329d0a25efa3889b66650f65de1de", - "commitDate": "2023-05-25", - "channel": "Nightly", - "short": "rustc 1.71.0-nightly (a2b1646c5 2023-05-25)" - }, - "contractCrate": { - "name": "multisig", - "version": "1.0.0", - "gitVersion": "v0.45.2.1-reproducible-169-g37d970c" - }, - "framework": { - "name": "multiversx-sc", - "version": "0.47.2" - } - }, - "docs": [ - "Multi-signature smart contract implementation.", - "Acts like a wallet that needs multiple signers for any action performed.", - "See the readme file for more detailed documentation." - ], "name": "Multisig", "constructor": { - "inputs": [ - { - "name": "quorum", - "type": "u32" - }, - { - "name": "board", - "type": "variadic
", - "multi_arg": true - } - ], - "outputs": [] - }, - "endpoints": [ - { - "name": "upgrade", - "mutability": "mutable", - "inputs": [], - "outputs": [] - }, - { - "docs": [ - "Allows the contract to receive funds even if it is marked as unpayable in the protocol." - ], - "name": "deposit", - "mutability": "mutable", - "payableInTokens": [ - "*" - ], - "inputs": [], - "outputs": [] - }, - { - "docs": [ - "Clears storage pertaining to an action that is no longer supposed to be executed.", - "Any signatures that the action received must first be removed, via `unsign`.", - "Otherwise this endpoint would be prone to abuse." - ], - "name": "discardAction", - "mutability": "mutable", - "inputs": [ - { - "name": "action_id", - "type": "u32" - } - ], - "outputs": [] - }, - { - "docs": [ - "Discard all the actions with the given IDs" - ], - "name": "discardBatch", - "mutability": "mutable", - "inputs": [ - { - "name": "action_ids", - "type": "variadic", - "multi_arg": true - } - ], - "outputs": [] - }, - { - "docs": [ - "Minimum number of signatures needed to perform any action." - ], - "name": "getQuorum", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "docs": [ - "Denormalized board member count.", - "It is kept in sync with the user list by the contract." - ], - "name": "getNumBoardMembers", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "name": "getNumGroups", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "docs": [ - "Denormalized proposer count.", - "It is kept in sync with the user list by the contract." - ], - "name": "getNumProposers", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "name": "getActionGroup", - "mutability": "readonly", - "inputs": [ - { - "name": "group_id", - "type": "u32" - } - ], - "outputs": [ - { - "type": "variadic", - "multi_result": true - } - ] - }, - { - "name": "getLastGroupActionId", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "docs": [ - "The index of the last proposed action.", - "0 means that no action was ever proposed yet." - ], - "name": "getActionLastIndex", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "docs": [ - "Initiates board member addition process.", - "Can also be used to promote a proposer to board member." - ], - "name": "proposeAddBoardMember", - "mutability": "mutable", - "inputs": [ - { - "name": "board_member_address", - "type": "Address" - } - ], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "docs": [ - "Initiates proposer addition process..", - "Can also be used to demote a board member to proposer." - ], - "name": "proposeAddProposer", - "mutability": "mutable", - "inputs": [ - { - "name": "proposer_address", - "type": "Address" - } - ], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "docs": [ - "Removes user regardless of whether it is a board member or proposer." - ], - "name": "proposeRemoveUser", - "mutability": "mutable", - "inputs": [ - { - "name": "user_address", - "type": "Address" - } - ], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "name": "proposeChangeQuorum", - "mutability": "mutable", - "inputs": [ - { - "name": "new_quorum", - "type": "u32" - } - ], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "docs": [ - "Propose a transaction in which the contract will perform a transfer-execute call.", - "Can send EGLD without calling anything.", - "Can call smart contract endpoints directly.", - "Doesn't really work with builtin functions." - ], - "name": "proposeTransferExecute", - "mutability": "mutable", - "inputs": [ - { - "name": "to", - "type": "Address" - }, - { - "name": "egld_amount", - "type": "BigUint" - }, - { - "name": "opt_gas_limit", - "type": "Option" - }, - { - "name": "function_call", - "type": "variadic", - "multi_arg": true - } - ], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "name": "proposeTransferExecuteEsdt", - "mutability": "mutable", - "inputs": [ - { - "name": "to", - "type": "Address" - }, - { - "name": "tokens", - "type": "List" - }, - { - "name": "opt_gas_limit", - "type": "Option" - }, - { - "name": "function_call", - "type": "variadic", - "multi_arg": true - } - ], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "docs": [ - "Propose a transaction in which the contract will perform an async call call.", - "Can call smart contract endpoints directly.", - "Can use ESDTTransfer/ESDTNFTTransfer/MultiESDTTransfer to send tokens, while also optionally calling endpoints.", - "Works well with builtin functions.", - "Cannot simply send EGLD directly without calling anything." - ], - "name": "proposeAsyncCall", - "mutability": "mutable", - "inputs": [ - { - "name": "to", - "type": "Address" - }, - { - "name": "egld_amount", - "type": "BigUint" - }, - { - "name": "opt_gas_limit", - "type": "Option" - }, - { - "name": "function_call", - "type": "variadic", - "multi_arg": true - } - ], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "name": "proposeSCDeployFromSource", - "mutability": "mutable", - "inputs": [ - { - "name": "amount", - "type": "BigUint" - }, - { - "name": "source", - "type": "Address" - }, - { - "name": "code_metadata", - "type": "CodeMetadata" - }, - { - "name": "arguments", - "type": "variadic", - "multi_arg": true - } - ], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "name": "proposeSCUpgradeFromSource", - "mutability": "mutable", - "inputs": [ - { - "name": "sc_address", - "type": "Address" - }, - { - "name": "amount", - "type": "BigUint" - }, - { - "name": "source", - "type": "Address" - }, - { - "name": "code_metadata", - "type": "CodeMetadata" - }, - { - "name": "arguments", - "type": "variadic", - "multi_arg": true - } - ], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "name": "proposeBatch", - "mutability": "mutable", - "inputs": [ - { - "name": "actions", - "type": "variadic", - "multi_arg": true - } - ], - "outputs": [ - { - "type": "u32" - } - ] - }, - { - "docs": [ - "Used by board members to sign actions." - ], - "name": "sign", - "mutability": "mutable", - "inputs": [ - { - "name": "action_id", - "type": "u32" - } - ], - "outputs": [] - }, + "inputs": [ { - "docs": [ - "Sign all the actions in the given batch" - ], - "name": "signBatch", - "mutability": "mutable", - "inputs": [ - { - "name": "group_id", - "type": "u32" - } - ], - "outputs": [] + "name": "quorum", + "type": "u32" }, { - "name": "signAndPerform", - "mutability": "mutable", - "inputs": [ - { - "name": "action_id", - "type": "u32" - } - ], - "outputs": [ - { - "type": "optional
", - "multi_result": true - } - ] - }, - { - "name": "signBatchAndPerform", - "mutability": "mutable", - "inputs": [ - { - "name": "group_id", - "type": "u32" - } - ], - "outputs": [] - }, - { - "docs": [ - "Board members can withdraw their signatures if they no longer desire for the action to be executed.", - "Actions that are left with no valid signatures can be then deleted to free up storage." - ], - "name": "unsign", - "mutability": "mutable", - "inputs": [ - { - "name": "action_id", - "type": "u32" - } - ], - "outputs": [] - }, - { - "docs": [ - "Unsign all actions with the given IDs" - ], - "name": "unsignBatch", - "mutability": "mutable", - "inputs": [ - { - "name": "group_id", - "type": "u32" - } - ], - "outputs": [] - }, - { - "docs": [ - "Returns `true` (`1`) if the user has signed the action.", - "Does not check whether or not the user is still a board member and the signature valid." - ], - "name": "signed", - "mutability": "readonly", - "inputs": [ - { - "name": "user", - "type": "Address" - }, - { - "name": "action_id", - "type": "u32" - } - ], - "outputs": [ - { - "type": "bool" - } - ] - }, - { - "name": "unsignForOutdatedBoardMembers", - "mutability": "mutable", - "inputs": [ - { - "name": "action_id", - "type": "u32" - }, - { - "name": "outdated_board_members", - "type": "variadic", - "multi_arg": true - } - ], - "outputs": [] - }, - { - "docs": [ - "Returns `true` (`1`) if `getActionValidSignerCount >= getQuorum`." - ], - "name": "quorumReached", - "mutability": "readonly", - "inputs": [ - { - "name": "action_id", - "type": "u32" - } - ], - "outputs": [ - { - "type": "bool" - } - ] - }, - { - "docs": [ - "Proposers and board members use this to launch signed actions." - ], - "name": "performAction", - "mutability": "mutable", - "inputs": [ - { - "name": "action_id", - "type": "u32" - } - ], - "outputs": [ - { - "type": "optional
", - "multi_result": true - } - ] - }, - { - "docs": [ - "Perform all the actions in the given batch" - ], - "name": "performBatch", - "mutability": "mutable", - "inputs": [ - { - "name": "group_id", - "type": "u32" - } - ], - "outputs": [] - }, - { - "name": "dnsRegister", - "onlyOwner": true, - "mutability": "mutable", - "payableInTokens": [ - "EGLD" - ], - "inputs": [ - { - "name": "dns_address", - "type": "Address" - }, - { - "name": "name", - "type": "bytes" - } - ], - "outputs": [] - }, - { - "docs": [ - "Iterates through all actions and retrieves those that are still pending.", - "Serialized full action data:", - "- the action id", - "- the serialized action data", - "- (number of signers followed by) list of signer addresses." - ], - "name": "getPendingActionFullInfo", - "mutability": "readonly", - "inputs": [ - { - "name": "opt_range", - "type": "optional>", - "multi_arg": true - } - ], - "outputs": [ - { - "type": "variadic", - "multi_result": true - } - ], - "labels": [ - "multisig-external-view" - ], - "allow_multiple_var_args": true - }, - { - "docs": [ - "Indicates user rights.", - "`0` = no rights,", - "`1` = can propose, but not sign,", - "`2` = can propose and sign." - ], - "name": "userRole", - "mutability": "readonly", - "inputs": [ - { - "name": "user", - "type": "Address" - } - ], - "outputs": [ - { - "type": "UserRole" - } - ], - "labels": [ - "multisig-external-view" - ] - }, - { - "docs": [ - "Lists all users that can sign actions." - ], - "name": "getAllBoardMembers", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "variadic
", - "multi_result": true - } - ], - "labels": [ - "multisig-external-view" - ] - }, - { - "docs": [ - "Lists all proposers that are not board members." - ], - "name": "getAllProposers", - "mutability": "readonly", - "inputs": [], - "outputs": [ - { - "type": "variadic
", - "multi_result": true - } - ], - "labels": [ - "multisig-external-view" - ] - }, - { - "docs": [ - "Serialized action data of an action with index." - ], - "name": "getActionData", - "mutability": "readonly", - "inputs": [ - { - "name": "action_id", - "type": "u32" - } - ], - "outputs": [ - { - "type": "Action" - } - ], - "labels": [ - "multisig-external-view" - ] - }, - { - "docs": [ - "Gets addresses of all users who signed an action.", - "Does not check if those users are still board members or not,", - "so the result may contain invalid signers." - ], - "name": "getActionSigners", - "mutability": "readonly", - "inputs": [ - { - "name": "action_id", - "type": "u32" - } - ], - "outputs": [ - { - "type": "List
" - } - ], - "labels": [ - "multisig-external-view" - ] - }, - { - "docs": [ - "Gets addresses of all users who signed an action and are still board members.", - "All these signatures are currently valid." - ], - "name": "getActionSignerCount", - "mutability": "readonly", - "inputs": [ - { - "name": "action_id", - "type": "u32" - } - ], - "outputs": [ - { - "type": "u32" - } - ], - "labels": [ - "multisig-external-view" - ] - }, - { - "docs": [ - "It is possible for board members to lose their role.", - "They are not automatically removed from all actions when doing so,", - "therefore the contract needs to re-check every time when actions are performed.", - "This function is used to validate the signers before performing an action.", - "It also makes it easy to check before performing an action." - ], - "name": "getActionValidSignerCount", - "mutability": "readonly", - "inputs": [ - { - "name": "action_id", - "type": "u32" - } - ], - "outputs": [ - { - "type": "u32" - } - ], - "labels": [ - "multisig-external-view" - ] + "name": "board", + "type": "variadic
", + "multi_arg": true } + ], + "outputs": [] + }, + "upgradeConstructor": { + "inputs": [], + "outputs": [] + }, + "endpoints": [ + { + "docs": [ + "Allows the contract to receive funds even if it is marked as unpayable in the protocol." + ], + "name": "deposit", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [], + "outputs": [] + }, + { + "docs": [ + "Clears storage pertaining to an action that is no longer supposed to be executed.", + "Any signatures that the action received must first be removed, via `unsign`.", + "Otherwise this endpoint would be prone to abuse." + ], + "name": "discardAction", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Discard all the actions with the given IDs" + ], + "name": "discardBatch", + "mutability": "mutable", + "inputs": [ + { + "name": "action_ids", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "docs": [ + "Minimum number of signatures needed to perform any action." + ], + "name": "getQuorum", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Denormalized board member count.", + "It is kept in sync with the user list by the contract." + ], + "name": "getNumBoardMembers", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "getNumGroups", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Denormalized proposer count.", + "It is kept in sync with the user list by the contract." + ], + "name": "getNumProposers", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "getActionGroup", + "mutability": "readonly", + "inputs": [ + { + "name": "group_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ] + }, + { + "name": "getLastGroupActionId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "The index of the last proposed action.", + "0 means that no action was ever proposed yet." + ], + "name": "getActionLastIndex", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Initiates board member addition process.", + "Can also be used to promote a proposer to board member." + ], + "name": "proposeAddBoardMember", + "mutability": "mutable", + "inputs": [ + { + "name": "board_member_address", + "type": "Address" + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Initiates proposer addition process..", + "Can also be used to demote a board member to proposer." + ], + "name": "proposeAddProposer", + "mutability": "mutable", + "inputs": [ + { + "name": "proposer_address", + "type": "Address" + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Removes user regardless of whether it is a board member or proposer." + ], + "name": "proposeRemoveUser", + "mutability": "mutable", + "inputs": [ + { + "name": "user_address", + "type": "Address" + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "proposeChangeQuorum", + "mutability": "mutable", + "inputs": [ + { + "name": "new_quorum", + "type": "u32" + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Propose a transaction in which the contract will perform a transfer-execute call.", + "Can send EGLD without calling anything.", + "Can call smart contract endpoints directly.", + "Doesn't really work with builtin functions." + ], + "name": "proposeTransferExecute", + "mutability": "mutable", + "inputs": [ + { + "name": "to", + "type": "Address" + }, + { + "name": "egld_amount", + "type": "BigUint" + }, + { + "name": "opt_gas_limit", + "type": "Option" + }, + { + "name": "function_call", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "proposeTransferExecuteEsdt", + "mutability": "mutable", + "inputs": [ + { + "name": "to", + "type": "Address" + }, + { + "name": "tokens", + "type": "List" + }, + { + "name": "opt_gas_limit", + "type": "Option" + }, + { + "name": "function_call", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Propose a transaction in which the contract will perform an async call call.", + "Can call smart contract endpoints directly.", + "Can use ESDTTransfer/ESDTNFTTransfer/MultiESDTTransfer to send tokens, while also optionally calling endpoints.", + "Works well with builtin functions.", + "Cannot simply send EGLD directly without calling anything." + ], + "name": "proposeAsyncCall", + "mutability": "mutable", + "inputs": [ + { + "name": "to", + "type": "Address" + }, + { + "name": "egld_amount", + "type": "BigUint" + }, + { + "name": "opt_gas_limit", + "type": "Option" + }, + { + "name": "function_call", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "proposeSCDeployFromSource", + "mutability": "mutable", + "inputs": [ + { + "name": "amount", + "type": "BigUint" + }, + { + "name": "source", + "type": "Address" + }, + { + "name": "code_metadata", + "type": "CodeMetadata" + }, + { + "name": "arguments", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "proposeSCUpgradeFromSource", + "mutability": "mutable", + "inputs": [ + { + "name": "sc_address", + "type": "Address" + }, + { + "name": "amount", + "type": "BigUint" + }, + { + "name": "source", + "type": "Address" + }, + { + "name": "code_metadata", + "type": "CodeMetadata" + }, + { + "name": "arguments", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "name": "proposeBatch", + "mutability": "mutable", + "inputs": [ + { + "name": "actions", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "u32" + } + ] + }, + { + "docs": [ + "Used by board members to sign actions." + ], + "name": "sign", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Sign all the actions in the given batch" + ], + "name": "signBatch", + "mutability": "mutable", + "inputs": [ + { + "name": "group_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "signAndPerform", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "optional
", + "multi_result": true + } + ] + }, + { + "name": "signBatchAndPerform", + "mutability": "mutable", + "inputs": [ + { + "name": "group_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Board members can withdraw their signatures if they no longer desire for the action to be executed.", + "Actions that are left with no valid signatures can be then deleted to free up storage." + ], + "name": "unsign", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Unsign all actions with the given IDs" + ], + "name": "unsignBatch", + "mutability": "mutable", + "inputs": [ + { + "name": "group_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "docs": [ + "Returns `true` (`1`) if the user has signed the action.", + "Does not check whether or not the user is still a board member and the signature valid." + ], + "name": "signed", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + }, + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "name": "unsignForOutdatedBoardMembers", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + }, + { + "name": "outdated_board_members", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "docs": [ + "Returns `true` (`1`) if `getActionValidSignerCount >= getQuorum`." + ], + "name": "quorumReached", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "bool" + } + ] + }, + { + "docs": [ + "Proposers and board members use this to launch signed actions." + ], + "name": "performAction", + "mutability": "mutable", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "optional
", + "multi_result": true + } + ] + }, + { + "docs": [ + "Perform all the actions in the given batch" + ], + "name": "performBatch", + "mutability": "mutable", + "inputs": [ + { + "name": "group_id", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "dnsRegister", + "onlyOwner": true, + "mutability": "mutable", + "payableInTokens": [ + "EGLD" + ], + "inputs": [ + { + "name": "dns_address", + "type": "Address" + }, + { + "name": "name", + "type": "bytes" + } + ], + "outputs": [] + }, + { + "docs": [ + "Iterates through all actions and retrieves those that are still pending.", + "Serialized full action data:", + "- the action id", + "- the serialized action data", + "- (number of signers followed by) list of signer addresses." + ], + "name": "getPendingActionFullInfo", + "mutability": "readonly", + "inputs": [ + { + "name": "opt_range", + "type": "optional>", + "multi_arg": true + } + ], + "outputs": [ + { + "type": "variadic", + "multi_result": true + } + ], + "labels": [ + "multisig-external-view" + ], + "allow_multiple_var_args": true + }, + { + "docs": [ + "Indicates user rights.", + "`0` = no rights,", + "`1` = can propose, but not sign,", + "`2` = can propose and sign." + ], + "name": "userRole", + "mutability": "readonly", + "inputs": [ + { + "name": "user", + "type": "Address" + } + ], + "outputs": [ + { + "type": "UserRole" + } + ], + "labels": [ + "multisig-external-view" + ] + }, + { + "docs": [ + "Lists all users that can sign actions." + ], + "name": "getAllBoardMembers", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic
", + "multi_result": true + } + ], + "labels": [ + "multisig-external-view" + ] + }, + { + "docs": [ + "Lists all proposers that are not board members." + ], + "name": "getAllProposers", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "variadic
", + "multi_result": true + } + ], + "labels": [ + "multisig-external-view" + ] + }, + { + "docs": [ + "Serialized action data of an action with index." + ], + "name": "getActionData", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "Action" + } + ], + "labels": [ + "multisig-external-view" + ] + }, + { + "docs": [ + "Gets addresses of all users who signed an action.", + "Does not check if those users are still board members or not,", + "so the result may contain invalid signers." + ], + "name": "getActionSigners", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "List
" + } + ], + "labels": [ + "multisig-external-view" + ] + }, + { + "docs": [ + "Gets addresses of all users who signed an action and are still board members.", + "All these signatures are currently valid." + ], + "name": "getActionSignerCount", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "u32" + } + ], + "labels": [ + "multisig-external-view" + ] + }, + { + "docs": [ + "It is possible for board members to lose their role.", + "They are not automatically removed from all actions when doing so,", + "therefore the contract needs to re-check every time when actions are performed.", + "This function is used to validate the signers before performing an action.", + "It also makes it easy to check before performing an action." + ], + "name": "getActionValidSignerCount", + "mutability": "readonly", + "inputs": [ + { + "name": "action_id", + "type": "u32" + } + ], + "outputs": [ + { + "type": "u32" + } + ], + "labels": [ + "multisig-external-view" + ] + } ], "events": [ - { - "identifier": "asyncCallSuccess", - "inputs": [ - { - "name": "results", - "type": "variadic", - "indexed": true - } - ] - }, - { - "identifier": "asyncCallError", - "inputs": [ - { - "name": "err_code", - "type": "u32", - "indexed": true - }, - { - "name": "err_message", - "type": "bytes", - "indexed": true - } - ] - }, - { - "identifier": "startPerformAction", - "inputs": [ - { - "name": "data", - "type": "ActionFullInfo" - } - ] - }, - { - "identifier": "performChangeUser", - "inputs": [ - { - "name": "action_id", - "type": "u32", - "indexed": true - }, - { - "name": "changed_user", - "type": "Address", - "indexed": true - }, - { - "name": "old_role", - "type": "UserRole", - "indexed": true - }, - { - "name": "new_role", - "type": "UserRole", - "indexed": true - } - ] - }, - { - "identifier": "performChangeQuorum", - "inputs": [ - { - "name": "action_id", - "type": "u32", - "indexed": true - }, - { - "name": "new_quorum", - "type": "u32", - "indexed": true - } - ] - }, - { - "identifier": "performAsyncCall", - "inputs": [ - { - "name": "action_id", - "type": "u32", - "indexed": true - }, - { - "name": "to", - "type": "Address", - "indexed": true - }, - { - "name": "egld_value", - "type": "BigUint", - "indexed": true - }, - { - "name": "gas", - "type": "u64", - "indexed": true - }, - { - "name": "endpoint", - "type": "bytes", - "indexed": true - }, - { - "name": "arguments", - "type": "variadic", - "indexed": true - } - ] - }, - { - "identifier": "performTransferExecuteEgld", - "inputs": [ - { - "name": "action_id", - "type": "u32", - "indexed": true - }, - { - "name": "to", - "type": "Address", - "indexed": true - }, - { - "name": "egld_value", - "type": "BigUint", - "indexed": true - }, - { - "name": "gas", - "type": "u64", - "indexed": true - }, - { - "name": "endpoint", - "type": "bytes", - "indexed": true - }, - { - "name": "arguments", - "type": "variadic", - "indexed": true - } - ] - }, - { - "identifier": "performTransferExecuteEsdt", - "inputs": [ - { - "name": "action_id", - "type": "u32", - "indexed": true - }, - { - "name": "to", - "type": "Address", - "indexed": true - }, - { - "name": "tokens", - "type": "List", - "indexed": true - }, - { - "name": "gas", - "type": "u64", - "indexed": true - }, - { - "name": "endpoint", - "type": "bytes", - "indexed": true - }, - { - "name": "arguments", - "type": "variadic", - "indexed": true - } - ] - }, - { - "identifier": "performDeployFromSource", - "inputs": [ - { - "name": "action_id", - "type": "u32", - "indexed": true - }, - { - "name": "egld_value", - "type": "BigUint", - "indexed": true - }, - { - "name": "source_address", - "type": "Address", - "indexed": true - }, - { - "name": "code_metadata", - "type": "CodeMetadata", - "indexed": true - }, - { - "name": "gas", - "type": "u64", - "indexed": true - }, - { - "name": "arguments", - "type": "variadic", - "indexed": true - } - ] - }, - { - "identifier": "performUpgradeFromSource", - "inputs": [ - { - "name": "action_id", - "type": "u32", - "indexed": true - }, - { - "name": "target_address", - "type": "Address", - "indexed": true - }, - { - "name": "egld_value", - "type": "BigUint", - "indexed": true - }, - { - "name": "source_address", - "type": "Address", - "indexed": true - }, - { - "name": "code_metadata", - "type": "CodeMetadata", - "indexed": true - }, - { - "name": "gas", - "type": "u64", - "indexed": true - }, - { - "name": "arguments", - "type": "variadic", - "indexed": true - } - ] - } + { + "identifier": "asyncCallSuccess", + "inputs": [ + { + "name": "results", + "type": "variadic", + "indexed": true + } + ] + }, + { + "identifier": "asyncCallError", + "inputs": [ + { + "name": "err_code", + "type": "u32", + "indexed": true + }, + { + "name": "err_message", + "type": "bytes", + "indexed": true + } + ] + }, + { + "identifier": "startPerformAction", + "inputs": [ + { + "name": "data", + "type": "ActionFullInfo" + } + ] + }, + { + "identifier": "performChangeUser", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "changed_user", + "type": "Address", + "indexed": true + }, + { + "name": "old_role", + "type": "UserRole", + "indexed": true + }, + { + "name": "new_role", + "type": "UserRole", + "indexed": true + } + ] + }, + { + "identifier": "performChangeQuorum", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "new_quorum", + "type": "u32", + "indexed": true + } + ] + }, + { + "identifier": "performAsyncCall", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "to", + "type": "Address", + "indexed": true + }, + { + "name": "egld_value", + "type": "BigUint", + "indexed": true + }, + { + "name": "gas", + "type": "u64", + "indexed": true + }, + { + "name": "endpoint", + "type": "bytes", + "indexed": true + }, + { + "name": "arguments", + "type": "variadic", + "indexed": true + } + ] + }, + { + "identifier": "performTransferExecuteEgld", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "to", + "type": "Address", + "indexed": true + }, + { + "name": "egld_value", + "type": "BigUint", + "indexed": true + }, + { + "name": "gas", + "type": "u64", + "indexed": true + }, + { + "name": "endpoint", + "type": "bytes", + "indexed": true + }, + { + "name": "arguments", + "type": "variadic", + "indexed": true + } + ] + }, + { + "identifier": "performTransferExecuteEsdt", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "to", + "type": "Address", + "indexed": true + }, + { + "name": "tokens", + "type": "List", + "indexed": true + }, + { + "name": "gas", + "type": "u64", + "indexed": true + }, + { + "name": "endpoint", + "type": "bytes", + "indexed": true + }, + { + "name": "arguments", + "type": "variadic", + "indexed": true + } + ] + }, + { + "identifier": "performDeployFromSource", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "egld_value", + "type": "BigUint", + "indexed": true + }, + { + "name": "source_address", + "type": "Address", + "indexed": true + }, + { + "name": "code_metadata", + "type": "CodeMetadata", + "indexed": true + }, + { + "name": "gas", + "type": "u64", + "indexed": true + }, + { + "name": "arguments", + "type": "variadic", + "indexed": true + } + ] + }, + { + "identifier": "performUpgradeFromSource", + "inputs": [ + { + "name": "action_id", + "type": "u32", + "indexed": true + }, + { + "name": "target_address", + "type": "Address", + "indexed": true + }, + { + "name": "egld_value", + "type": "BigUint", + "indexed": true + }, + { + "name": "source_address", + "type": "Address", + "indexed": true + }, + { + "name": "code_metadata", + "type": "CodeMetadata", + "indexed": true + }, + { + "name": "gas", + "type": "u64", + "indexed": true + }, + { + "name": "arguments", + "type": "variadic", + "indexed": true + } + ] + } ], "esdtAttributes": [], "hasCallback": true, "types": { - "Action": { - "type": "enum", - "variants": [ - { - "name": "Nothing", - "discriminant": 0 - }, - { - "name": "AddBoardMember", - "discriminant": 1, - "fields": [ - { - "name": "0", - "type": "Address" - } - ] - }, - { - "name": "AddProposer", - "discriminant": 2, - "fields": [ - { - "name": "0", - "type": "Address" - } - ] - }, - { - "name": "RemoveUser", - "discriminant": 3, - "fields": [ - { - "name": "0", - "type": "Address" - } - ] - }, - { - "name": "ChangeQuorum", - "discriminant": 4, - "fields": [ - { - "name": "0", - "type": "u32" - } - ] - }, - { - "name": "SendTransferExecuteEgld", - "discriminant": 5, - "fields": [ - { - "name": "0", - "type": "CallActionData" - } - ] - }, - { - "name": "SendTransferExecuteEsdt", - "discriminant": 6, - "fields": [ - { - "name": "0", - "type": "EsdtTransferExecuteData" - } - ] - }, - { - "name": "SendAsyncCall", - "discriminant": 7, - "fields": [ - { - "name": "0", - "type": "CallActionData" - } - ] - }, - { - "name": "SCDeployFromSource", - "discriminant": 8, - "fields": [ - { - "name": "amount", - "type": "BigUint" - }, - { - "name": "source", - "type": "Address" - }, - { - "name": "code_metadata", - "type": "CodeMetadata" - }, - { - "name": "arguments", - "type": "List" - } - ] - }, - { - "name": "SCUpgradeFromSource", - "discriminant": 9, - "fields": [ - { - "name": "sc_address", - "type": "Address" - }, - { - "name": "amount", - "type": "BigUint" - }, - { - "name": "source", - "type": "Address" - }, - { - "name": "code_metadata", - "type": "CodeMetadata" - }, - { - "name": "arguments", - "type": "List" - } - ] - } + "Action": { + "type": "enum", + "variants": [ + { + "name": "Nothing", + "discriminant": 0 + }, + { + "name": "AddBoardMember", + "discriminant": 1, + "fields": [ + { + "name": "0", + "type": "Address" + } ] - }, - "ActionFullInfo": { - "type": "struct", - "docs": [ - "Not used internally, just to retrieve results via endpoint." - ], + }, + { + "name": "AddProposer", + "discriminant": 2, "fields": [ - { - "name": "action_id", - "type": "u32" - }, - { - "name": "group_id", - "type": "u32" - }, - { - "name": "action_data", - "type": "Action" - }, - { - "name": "signers", - "type": "List
" - } + { + "name": "0", + "type": "Address" + } ] - }, - "ActionStatus": { - "type": "enum", - "variants": [ - { - "name": "Available", - "discriminant": 0 - }, - { - "name": "Aborted", - "discriminant": 1 - } + }, + { + "name": "RemoveUser", + "discriminant": 3, + "fields": [ + { + "name": "0", + "type": "Address" + } ] - }, - "CallActionData": { - "type": "struct", + }, + { + "name": "ChangeQuorum", + "discriminant": 4, "fields": [ - { - "name": "to", - "type": "Address" - }, - { - "name": "egld_amount", - "type": "BigUint" - }, - { - "name": "opt_gas_limit", - "type": "Option" - }, - { - "name": "endpoint_name", - "type": "bytes" - }, - { - "name": "arguments", - "type": "List" - } + { + "name": "0", + "type": "u32" + } ] - }, - "EsdtTokenPayment": { - "type": "struct", + }, + { + "name": "SendTransferExecuteEgld", + "discriminant": 5, "fields": [ - { - "name": "token_identifier", - "type": "TokenIdentifier" - }, - { - "name": "token_nonce", - "type": "u64" - }, - { - "name": "amount", - "type": "BigUint" - } + { + "name": "0", + "type": "CallActionData" + } ] - }, - "EsdtTransferExecuteData": { - "type": "struct", + }, + { + "name": "SendTransferExecuteEsdt", + "discriminant": 6, "fields": [ - { - "name": "to", - "type": "Address" - }, - { - "name": "tokens", - "type": "List" - }, - { - "name": "opt_gas_limit", - "type": "Option" - }, - { - "name": "endpoint_name", - "type": "bytes" - }, - { - "name": "arguments", - "type": "List" - } + { + "name": "0", + "type": "EsdtTransferExecuteData" + } ] - }, - "UserRole": { - "type": "enum", - "variants": [ - { - "name": "None", - "discriminant": 0 - }, - { - "name": "Proposer", - "discriminant": 1 - }, - { - "name": "BoardMember", - "discriminant": 2 - } + }, + { + "name": "SendAsyncCall", + "discriminant": 7, + "fields": [ + { + "name": "0", + "type": "CallActionData" + } ] - } + }, + { + "name": "SCDeployFromSource", + "discriminant": 8, + "fields": [ + { + "name": "amount", + "type": "BigUint" + }, + { + "name": "source", + "type": "Address" + }, + { + "name": "code_metadata", + "type": "CodeMetadata" + }, + { + "name": "arguments", + "type": "List" + } + ] + }, + { + "name": "SCUpgradeFromSource", + "discriminant": 9, + "fields": [ + { + "name": "sc_address", + "type": "Address" + }, + { + "name": "amount", + "type": "BigUint" + }, + { + "name": "source", + "type": "Address" + }, + { + "name": "code_metadata", + "type": "CodeMetadata" + }, + { + "name": "arguments", + "type": "List" + } + ] + } + ] + }, + "ActionFullInfo": { + "type": "struct", + "docs": [ + "Not used internally, just to retrieve results via endpoint." + ], + "fields": [ + { + "name": "action_id", + "type": "u32" + }, + { + "name": "group_id", + "type": "u32" + }, + { + "name": "action_data", + "type": "Action" + }, + { + "name": "signers", + "type": "List
" + } + ] + }, + "ActionStatus": { + "type": "enum", + "variants": [ + { + "name": "Available", + "discriminant": 0 + }, + { + "name": "Aborted", + "discriminant": 1 + } + ] + }, + "CallActionData": { + "type": "struct", + "fields": [ + { + "name": "to", + "type": "Address" + }, + { + "name": "egld_amount", + "type": "BigUint" + }, + { + "name": "opt_gas_limit", + "type": "Option" + }, + { + "name": "endpoint_name", + "type": "bytes" + }, + { + "name": "arguments", + "type": "List" + } + ] + }, + "EsdtTokenPayment": { + "type": "struct", + "fields": [ + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "token_nonce", + "type": "u64" + }, + { + "name": "amount", + "type": "BigUint" + } + ] + }, + "EsdtTransferExecuteData": { + "type": "struct", + "fields": [ + { + "name": "to", + "type": "Address" + }, + { + "name": "tokens", + "type": "List" + }, + { + "name": "opt_gas_limit", + "type": "Option" + }, + { + "name": "endpoint_name", + "type": "bytes" + }, + { + "name": "arguments", + "type": "List" + } + ] + }, + "UserRole": { + "type": "enum", + "variants": [ + { + "name": "None", + "discriminant": 0 + }, + { + "name": "Proposer", + "discriminant": 1 + }, + { + "name": "BoardMember", + "discriminant": 2 + } + ] + } } -} + } \ No newline at end of file From d9fd92ae910a69b541ed6085fd7335ac03a1aa47 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 15 Apr 2025 10:45:11 +0300 Subject: [PATCH 02/14] Remove unused variables --- src/multisig/multisigTransactionsFactory.spec.ts | 1 - src/multisig/multisigTransactionsFactory.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/multisig/multisigTransactionsFactory.spec.ts b/src/multisig/multisigTransactionsFactory.spec.ts index 97bc2900a..e177b1795 100644 --- a/src/multisig/multisigTransactionsFactory.spec.ts +++ b/src/multisig/multisigTransactionsFactory.spec.ts @@ -45,7 +45,6 @@ describe("test multisig transactions factory", function () { board, amount, }); - const res = Buffer.from(transaction.data).toString().split("@"); assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu"); diff --git a/src/multisig/multisigTransactionsFactory.ts b/src/multisig/multisigTransactionsFactory.ts index aa5ba209a..9bd5ac0c2 100644 --- a/src/multisig/multisigTransactionsFactory.ts +++ b/src/multisig/multisigTransactionsFactory.ts @@ -204,7 +204,6 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor const tokenComputer = new TokenComputer(); const argsTyped = []; for (const token of options.tokens) { - const identifier = tokenComputer.extractIdentifierFromExtendedIdentifier(token.token.identifier); argsTyped.push({ token_identifier: new TokenIdentifierValue( tokenComputer.extractIdentifierFromExtendedIdentifier(token.token.identifier), From 33906160cacb6407526a289a73a00f30ce8dceed Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 15 Apr 2025 11:06:35 +0300 Subject: [PATCH 03/14] remove empty comment --- src/multisig/resources.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/multisig/resources.ts b/src/multisig/resources.ts index 04c49a6d2..8da1888ed 100644 --- a/src/multisig/resources.ts +++ b/src/multisig/resources.ts @@ -209,9 +209,6 @@ export class ProposeAsyncCallInput { functionArguments: any[]; gasLimit: bigint; abi?: Abi; - /** - * - */ constructor(options: { multisigContract: Address; to: Address; From 0f558478075873022889151792c52fc1a3fabd46 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 15 Apr 2025 11:33:16 +0300 Subject: [PATCH 04/14] Remove unused resources --- src/multisig/resources.ts | 165 +------------------------------------- 1 file changed, 1 insertion(+), 164 deletions(-) diff --git a/src/multisig/resources.ts b/src/multisig/resources.ts index 8da1888ed..29dac7b5b 100644 --- a/src/multisig/resources.ts +++ b/src/multisig/resources.ts @@ -1,5 +1,5 @@ import { Abi, BytesValue } from "../abi"; -import { Token, TokenTransfer, TransactionsFactoryConfig } from "../core"; +import { TokenTransfer, TransactionsFactoryConfig } from "../core"; import { Address } from "../core/address"; import { CodeMetadata } from "../core/codeMetadata"; import { ARGUMENTS_SEPARATOR } from "../core/constants"; @@ -18,15 +18,6 @@ export type DeployMultisigContractInput = { gasLimit: bigint; }; -export type UpgradeMultisigContractInput = { - bytecode: Uint8Array; - isUpgradeable?: boolean; - isReadable?: boolean; - isPayable?: boolean; - isPayableBySmartContract?: boolean; - multisigContract: Address; - gasLimit: bigint; -}; export type MultisigContractInput = { multisigContract: Address; gasLimit: bigint; @@ -261,152 +252,6 @@ export type DiscardBatchInput = MultisigContractInput & { actionIds: number[]; }; -export enum UserRoleEnum { - None = "None", - Proposer = "Proposer", - BoardMember = "BoardMember", -} - -export enum MultisigActionEnum { - Nothing = "Nothing", - AddBoardMember = "AddBoardMember", - AddProposer = "AddProposer", - RemoveUser = "RemoveUser", - ChangeQuorum = "ChangeQuorum", - SendTransferExecuteEgld = "SendTransferExecuteEgld", - SendTransferExecuteEsdt = "SendTransferExecuteEsdt", - SendAsyncCall = "SendAsyncCall", - SCDeployFromSource = "SCDeployFromSource", - SCUpgradeFromSource = "SCUpgradeFromSource", -} - -export class MultisigAction { - public type: MultisigActionEnum = MultisigActionEnum.Nothing; -} -export class AddBoardMember extends MultisigAction { - public address: Address; - constructor(address: Address) { - super(); - this.type = MultisigActionEnum.AddBoardMember; - this.address = address; - } -} -export class AddProposer extends MultisigAction { - public address: Address; - constructor(address: Address) { - super(); - this.type = MultisigActionEnum.AddProposer; - this.address = address; - } -} -export class RemoveUser extends MultisigAction { - public type: MultisigActionEnum = MultisigActionEnum.RemoveUser; - public address: Address; - constructor(address: Address) { - super(); - this.type = MultisigActionEnum.RemoveUser; - this.address = address; - } -} - -export class ChangeQuorum extends MultisigAction { - public quorum: number; - constructor(quorum: number) { - super(); - this.type = MultisigActionEnum.ChangeQuorum; - this.quorum = quorum; - } -} - -export class SendTransferExecuteEgld extends MultisigAction { - receiver: Address; - amount: bigint; - optionalGasLimit?: number; - funcionName: string; - arguments: Uint8Array[]; - constructor(data: any) { - super(); - this.type = MultisigActionEnum.SendTransferExecuteEgld; - this.receiver = data.to; - this.amount = data.egld_amount; - this.optionalGasLimit = data.opt_gas_limit; - this.funcionName = data.endpoint_name.toString(); - this.arguments = data.arguments; - } -} -export class SendTransferExecuteEsdt extends MultisigAction { - receiver: Address; - tokens: TokenTransfer[]; - optionalGasLimit?: number; - funcionName: string; - arguments: Uint8Array[]; - constructor(data: any) { - super(); - this.type = MultisigActionEnum.SendTransferExecuteEsdt; - this.receiver = data.to; - this.tokens = data.tokens.map( - (token: { token_identifier: any; nonce: any; amount: any }) => - new TokenTransfer({ - token: new Token({ identifier: token.token_identifier, nonce: token.nonce }), - amount: token.amount, - }), - ); - this.optionalGasLimit = data.opt_gas_limit; - - this.funcionName = Buffer.from(data.endpoint_name.toString(), "hex").toString(); - this.arguments = data.arguments; - } -} - -export class SendAsyncCall extends MultisigAction { - receiver: Address; - amount: bigint; - optionalGasLimit?: number; - funcionName: string; - arguments: Uint8Array[]; - constructor(data: any) { - super(); - this.type = MultisigActionEnum.SendAsyncCall; - this.receiver = data.to; - this.amount = data.egld_amount; - this.optionalGasLimit = data.opt_gas_limit; - this.funcionName = data.endpoint_name.toString(); - this.arguments = data.arguments; - } -} - -export class SCDeployFromSource extends MultisigAction { - sourceContractAddress: Address; - amount: bigint; - codeMetadata: CodeMetadata; - arguments: Uint8Array[]; - constructor(data: any) { - super(); - this.type = MultisigActionEnum.SCDeployFromSource; - this.sourceContractAddress = data[1]; - this.amount = data[0]; - this.codeMetadata = data[2]; - this.arguments = data[3]; - } -} - -export class SCUpgradeFromSource extends MultisigAction { - sourceContractAddress: Address; - scAddress: Address; - amount: bigint; - codeMetadata: CodeMetadata; - arguments: Uint8Array[]; - constructor(data: any) { - super(); - this.type = MultisigActionEnum.SCUpgradeFromSource; - this.scAddress = data[0]; - this.amount = data[1]; - this.sourceContractAddress = data[2]; - this.codeMetadata = data[3]; - this.arguments = data[4]; - } -} - export type CallActionData = { receiver: Address; amount: bigint; @@ -420,11 +265,3 @@ export type EsdtTokenPayment = { token_nonce: any; amount: any; }; - -export type EsdtTransferExecuteData = { - to: Address; - tokens: EsdtTokenPayment[]; - opt_gas_limit?: number | null; - endpoint_name: Uint8Array; - arguments: Uint8Array[]; -}; From 93569cd8cdb6a9a8f14052a4b1474ad1fc046974 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 15 Apr 2025 15:26:47 +0300 Subject: [PATCH 05/14] Update resources --- src/index.ts | 1 + src/multisig/index.ts | 3 + .../multisigTransactionsFactory.spec.ts | 24 +-- src/multisig/multisigTransactionsFactory.ts | 83 +++++---- .../proposeTransferExecuteContract.ts | 90 +++++++++ src/multisig/resources.ts | 174 ++---------------- 6 files changed, 159 insertions(+), 216 deletions(-) create mode 100644 src/multisig/index.ts create mode 100644 src/multisig/proposeTransferExecuteContract.ts diff --git a/src/index.ts b/src/index.ts index b9e1ef27e..cb9f585e8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ export * from "./accounts"; export * from "./core"; export * from "./delegation"; export * from "./entrypoints"; +export * from "./multisig"; export * from "./networkProviders"; export * from "./smartContracts"; export * from "./tokenManagement"; diff --git a/src/multisig/index.ts b/src/multisig/index.ts new file mode 100644 index 000000000..5185e2f02 --- /dev/null +++ b/src/multisig/index.ts @@ -0,0 +1,3 @@ +export * from "./multisigTransactionsFactory"; +export * from "./proposeTransferExecuteContract"; +export * from "./resources"; diff --git a/src/multisig/multisigTransactionsFactory.spec.ts b/src/multisig/multisigTransactionsFactory.spec.ts index e177b1795..05f4c1e29 100644 --- a/src/multisig/multisigTransactionsFactory.spec.ts +++ b/src/multisig/multisigTransactionsFactory.spec.ts @@ -30,12 +30,10 @@ describe("test multisig transactions factory", function () { it("should create transaction for deploy multisig contract", function () { const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - const boardMemberAddress = Address.newFromBech32( - "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - ); - const proposerAddress = Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"); + const boardMemberOne = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const boardMemberTwo = Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"); - const board = [boardMemberAddress, proposerAddress]; + const board = [boardMemberOne, boardMemberTwo]; const amount = 1000000000000000000n; // 1 EGLD const transaction = factory.createTransactionForMultisigDeploy(senderAddress, { @@ -61,16 +59,14 @@ describe("test multisig transactions factory", function () { it("should create transaction for propose add board member", function () { const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - const boardMemberAddress = Address.newFromBech32( - "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", - ); + const boardMember = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); const multisigContractAddress = Address.newFromBech32( "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", ); const transaction = factory.createTransactionForProposeAddBoardMember(senderAddress, { multisigContract: multisigContractAddress, gasLimit: 5000000n, - boardMemberAddress: boardMemberAddress, + boardMember: boardMember, }); assert.instanceOf(transaction, Transaction); @@ -87,14 +83,14 @@ describe("test multisig transactions factory", function () { it("should create transaction for propose add proposer", function () { const senderAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); - const proposerAddress = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); + const proposer = Address.newFromBech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"); const multisigContractAddress = Address.newFromBech32( "erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqylllslmq6y6", ); const transaction = factory.createTransactionForProposeAddProposer(senderAddress, { multisigContract: multisigContractAddress, gasLimit: 5000000n, - proposerAddress: proposerAddress, + proposer: proposer, }); assert.instanceOf(transaction, Transaction); @@ -161,7 +157,7 @@ describe("test multisig transactions factory", function () { const transaction = factory.createTransactionForProposeTransferExecute(senderAddress, { multisigContract: multisigContractAddress, gasLimit: 5000000n, - egldAmount: amount, + nativeTokenAmount: amount, to: destinationContract, functionName: "add", functionArguments: [7], @@ -253,7 +249,7 @@ describe("test multisig transactions factory", function () { const transaction = factory.createTransactionForDeposit(senderAddress, { multisigContract: multisigContractAddress, gasLimit: 5000000n, - egldAmount: 1n, + nativeTokenAmount: 1n, tokenTransfers: [], }); @@ -280,7 +276,7 @@ describe("test multisig transactions factory", function () { const transaction = factory.createTransactionForDeposit(senderAddress, { multisigContract: multisigContractAddress, gasLimit: 5000000n, - egldAmount: 0n, + nativeTokenAmount: 0n, tokenTransfers: [tokenTransfer], }); diff --git a/src/multisig/multisigTransactionsFactory.ts b/src/multisig/multisigTransactionsFactory.ts index 9bd5ac0c2..e564b0e55 100644 --- a/src/multisig/multisigTransactionsFactory.ts +++ b/src/multisig/multisigTransactionsFactory.ts @@ -2,7 +2,6 @@ import { AddressValue, ArgSerializer, BigUIntValue, - ContractFunction, EndpointDefinition, EndpointModifiers, NativeSerializer, @@ -19,13 +18,14 @@ import { Address } from "../core/address"; import { Transaction } from "../core/transaction"; import { TransactionBuilder } from "../core/transactionBuilder"; import { SmartContractTransactionsFactory } from "../smartContracts"; +import { ProposeTransferExecuteContractInput } from "./proposeTransferExecuteContract"; import * as resources from "./resources"; interface IAbi { constructorDefinition: EndpointDefinition; upgradeConstructorDefinition?: EndpointDefinition; - getEndpoint(name: string | ContractFunction): EndpointDefinition; + getEndpoint(name: string): EndpointDefinition; } /** * Use this class to create multisig related transactions like creating a new multisig contract, @@ -68,7 +68,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor ): Transaction { const dataParts = [ "proposeAddBoardMember", - this.argSerializer.valuesToStrings([new AddressValue(options.boardMemberAddress)])[0], + this.argSerializer.valuesToStrings([new AddressValue(options.boardMember)])[0], ]; return new TransactionBuilder({ @@ -77,7 +77,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -87,7 +87,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor createTransactionForProposeAddProposer(sender: Address, options: resources.ProposeAddProposerInput): Transaction { const dataParts = [ "proposeAddProposer", - this.argSerializer.valuesToStrings([new AddressValue(options.proposerAddress)])[0], + this.argSerializer.valuesToStrings([new AddressValue(options.proposer)])[0], ]; return new TransactionBuilder({ @@ -96,7 +96,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -115,7 +115,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -134,7 +134,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -146,19 +146,20 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor options: resources.ProposeTransferExecuteInput, ): Transaction { const gasOption = new U64Value(options.gasLimit); - const input = resources.ProposeTransferExecutInput.newFromTransferExecuteInput({ + const input = ProposeTransferExecuteContractInput.newFromTransferExecuteInput({ multisig: options.multisigContract, to: options.to, - tokenTransfers: [], functionName: options.functionName, arguments: options.functionArguments, abi: options.abi, }); const dataParts = [ "proposeTransferExecute", - this.argSerializer.valuesToStrings([new AddressValue(options.to)])[0], - this.argSerializer.valuesToStrings([new BigUIntValue(options.egldAmount)])[0], - this.argSerializer.valuesToStrings([new OptionValue(new OptionType(new U64Type()), gasOption)])[0], + ...this.argSerializer.valuesToStrings([ + new AddressValue(options.to), + new BigUIntValue(options.nativeTokenAmount), + new OptionValue(new OptionType(new U64Type()), gasOption), + ]), ...input.functionCall, ]; @@ -168,7 +169,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -181,7 +182,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor function: "deposit", gasLimit: options.gasLimit ?? 0n, arguments: [], - nativeTransferAmount: options.egldAmount, + nativeTransferAmount: options.nativeTokenAmount, tokenTransfers: options.tokenTransfers, }); } @@ -193,10 +194,9 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor sender: Address, options: resources.ProposeTransferExecuteEsdtInput, ): Transaction { - const input = resources.ProposeTransferExecutInput.newFromTransferExecuteInput({ + const input = ProposeTransferExecuteContractInput.newFromTransferExecuteInput({ multisig: options.multisigContract, to: options.to, - tokenTransfers: [], functionName: options.functionName, arguments: options.functionArguments, abi: options.abi, @@ -228,7 +228,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -236,7 +236,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor * Proposes an async call to another contract */ createTransactionForProposeAsyncCall(sender: Address, options: resources.ProposeAsyncCallInput): Transaction { - const input = resources.ProposeTransferExecutInput.newFromTransferExecuteInput({ + const input = ProposeTransferExecuteContractInput.newFromProposeAsyncCallInput({ multisig: options.multisigContract, to: options.to, tokenTransfers: options.tokenTransfers, @@ -247,9 +247,11 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor let receiver = options.multisigContract; const dataParts = [ "proposeAsyncCall", - this.argSerializer.valuesToStrings([new AddressValue(options.to)])[0], - this.argSerializer.valuesToStrings([new BigUIntValue(options.nativeTransferAmount)])[0], - this.argSerializer.valuesToStrings([new BigUIntValue(options.gasLimit ?? 0n)])[0], + ...this.argSerializer.valuesToStrings([ + new AddressValue(options.to), + new BigUIntValue(options.nativeTransferAmount), + new BigUIntValue(options.gasLimit ?? 0n), + ]), ...input.functionCall, ]; @@ -273,8 +275,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor ): Transaction { const dataParts = [ "proposeSCDeployFromSource", - this.argSerializer.valuesToStrings([new BigUIntValue(options.amount)])[0], - this.argSerializer.valuesToStrings([new AddressValue(options.source)])[0], + ...this.argSerializer.valuesToStrings([new BigUIntValue(options.amount), new AddressValue(options.source)]), options.codeMetadata.toString(), ...options.arguments, ]; @@ -285,7 +286,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -298,9 +299,11 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor ): Transaction { const dataParts = [ "proposeSCUpgradeFromSource", - this.argSerializer.valuesToStrings([new AddressValue(options.scAddress)])[0], - this.argSerializer.valuesToStrings([new BigUIntValue(options.amount)])[0], - this.argSerializer.valuesToStrings([new AddressValue(options.source)])[0], + ...this.argSerializer.valuesToStrings([ + new AddressValue(options.scAddress), + new BigUIntValue(options.amount), + new AddressValue(options.source), + ]), options.codeMetadata.toString(), ...options.arguments, ]; @@ -311,7 +314,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -327,7 +330,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -343,7 +346,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -359,7 +362,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -378,7 +381,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -394,7 +397,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -410,7 +413,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -437,7 +440,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -453,7 +456,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -469,7 +472,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -485,7 +488,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } @@ -503,7 +506,7 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor receiver: options.multisigContract, dataParts: dataParts, gasLimit: options.gasLimit, - addDataMovementGas: true, + addDataMovementGas: false, }).build(); } } diff --git a/src/multisig/proposeTransferExecuteContract.ts b/src/multisig/proposeTransferExecuteContract.ts new file mode 100644 index 000000000..c6fcb78be --- /dev/null +++ b/src/multisig/proposeTransferExecuteContract.ts @@ -0,0 +1,90 @@ +import { Abi, BytesValue } from "../abi"; +import { Address, TokenTransfer, TransactionsFactoryConfig } from "../core"; +import { ARGUMENTS_SEPARATOR } from "../core/constants"; +import { utf8ToHex } from "../core/utils.codec"; +import { SmartContractTransactionsFactory } from "../smartContracts"; + +export class ProposeTransferExecuteContractInput { + multisigContract: Address; + to: Address; + gasLimit?: bigint; + functionCall: any[]; + + constructor(options: { multisigContract: Address; to: Address; gasLimit?: bigint; functionCall: any[] }) { + this.multisigContract = options.multisigContract; + this.to = options.to; + this.gasLimit = options.gasLimit; + this.functionCall = options.functionCall; + } + + static newFromTransferExecuteInput(options: { + multisig: Address; + to: Address; + functionName: string; + arguments: any[]; + optGasLimit?: bigint; + abi?: Abi; + }): ProposeTransferExecuteContractInput { + const transactionsFactory = new SmartContractTransactionsFactory({ + config: new TransactionsFactoryConfig({ chainID: "" }), + abi: options.abi, + }); + const transaction = transactionsFactory.createTransactionForExecute(Address.empty(), { + contract: Address.empty(), + function: options.functionName, + gasLimit: 0n, + arguments: options.arguments, + nativeTransferAmount: 0n, + }); + const functionCallParts = Buffer.from(transaction.data).toString().split(ARGUMENTS_SEPARATOR); + const functionName = functionCallParts[0]; + const functionArguments = []; + for (let index = 1; index < functionCallParts.length; index++) { + const element = functionCallParts[index]; + functionArguments.push(element.valueOf()); + } + const functionCall = [new BytesValue(Buffer.from(utf8ToHex(functionName))), ...functionArguments]; + return new ProposeTransferExecuteContractInput({ + multisigContract: options.multisig, + to: options.to, + functionCall: functionCall, + gasLimit: options.optGasLimit, + }); + } + + static newFromProposeAsyncCallInput(options: { + multisig: Address; + to: Address; + tokenTransfers: TokenTransfer[]; + functionName: string; + arguments: any[]; + optGasLimit?: bigint; + abi?: Abi; + }): ProposeTransferExecuteContractInput { + const transactionsFactory = new SmartContractTransactionsFactory({ + config: new TransactionsFactoryConfig({ chainID: "" }), + abi: options.abi, + }); + const transaction = transactionsFactory.createTransactionForExecute(Address.empty(), { + contract: Address.empty(), + function: options.functionName, + gasLimit: 0n, + arguments: options.arguments, + nativeTransferAmount: 0n, + }); + const functionCallParts = Buffer.from(transaction.data).toString().split(ARGUMENTS_SEPARATOR); + const functionName = functionCallParts[0]; + const functionArguments = []; + for (let index = 1; index < functionCallParts.length; index++) { + const element = functionCallParts[index]; + functionArguments.push(element.valueOf()); + } + const functionCall = [new BytesValue(Buffer.from(utf8ToHex(functionName))), ...functionArguments]; + return new ProposeTransferExecuteContractInput({ + multisigContract: options.multisig, + to: options.to, + functionCall: functionCall, + gasLimit: options.optGasLimit, + }); + } +} diff --git a/src/multisig/resources.ts b/src/multisig/resources.ts index 29dac7b5b..e0b67be5e 100644 --- a/src/multisig/resources.ts +++ b/src/multisig/resources.ts @@ -1,10 +1,7 @@ -import { Abi, BytesValue } from "../abi"; -import { TokenTransfer, TransactionsFactoryConfig } from "../core"; +import { Abi } from "../abi"; +import { TokenTransfer } from "../core"; import { Address } from "../core/address"; import { CodeMetadata } from "../core/codeMetadata"; -import { ARGUMENTS_SEPARATOR } from "../core/constants"; -import { utf8ToHex } from "../core/utils.codec"; -import { SmartContractTransactionsFactory } from "../smartContracts"; export type DeployMultisigContractInput = { quorum: number; @@ -24,11 +21,11 @@ export type MultisigContractInput = { }; export type ProposeAddBoardMemberInput = MultisigContractInput & { - boardMemberAddress: Address; + boardMember: Address; }; export type ProposeAddProposerInput = MultisigContractInput & { - proposerAddress: Address; + proposer: Address; }; export type ProposeRemoveUserInput = MultisigContractInput & { @@ -41,157 +38,29 @@ export type ProposeChangeQuorumInput = MultisigContractInput & { export type ProposeTransferExecuteInput = MultisigContractInput & { to: Address; - egldAmount: bigint; - gasLimit?: bigint; + nativeTokenAmount: bigint; + optGasLimit?: bigint; functionName: string; functionArguments: any[]; abi?: Abi; }; export type DepositExecuteInput = MultisigContractInput & { - egldAmount: bigint; + nativeTokenAmount: bigint; gasLimit?: bigint; tokenTransfers: TokenTransfer[]; }; -export class ProposeTransferExecuteEsdtInput { - multisigContract: Address; +export type ProposeTransferExecuteEsdtInput = MultisigContractInput & { to: Address; tokens: any[]; - gasLimit: bigint; + optGasLimit?: bigint; functionName: string; functionArguments: any[]; abi?: Abi; +}; - constructor(options: ProposeTransferExecuteEsdtInput) { - this.multisigContract = options.multisigContract; - this.to = options.to; - this.tokens = options.tokens; - this.gasLimit = options.gasLimit; - this.functionName = options.functionName; - this.functionArguments = options.functionArguments; - this.abi = options.abi; - } -} - -export class ProposeTransferExecuteEsdtInputForContract { - multisigContract: Address; - to: Address; - gasLimit?: bigint; - functionCall: any[]; - tokens: EsdtTokenPayment[]; - - constructor(options: { - multisigContract: Address; - to: Address; - gasLimit?: bigint; - functionCall: any[]; - tokens: EsdtTokenPayment[]; - }) { - this.multisigContract = options.multisigContract; - this.to = options.to; - this.gasLimit = options.gasLimit; - this.functionCall = options.functionCall; - this.tokens = options.tokens; - } - - static newFromTransferExecuteInput(options: { - multisig: Address; - to: Address; - nativeTransferAmount: bigint; - tokenTransfers: TokenTransfer[]; - functionName: string; - arguments: any[]; - optGasLimit?: bigint; - abi?: Abi; - }): ProposeTransferExecuteEsdtInputForContract { - const transactionsFactory = new SmartContractTransactionsFactory({ - config: new TransactionsFactoryConfig({ chainID: "" }), - abi: options.abi, - }); - const transaction = transactionsFactory.createTransactionForExecute(Address.empty(), { - contract: Address.empty(), - function: options.functionName, - gasLimit: 0n, - arguments: options.arguments, - nativeTransferAmount: 0n, - tokenTransfers: options.tokenTransfers, - }); - - const tokens: EsdtTokenPayment[] = options.tokenTransfers.map((token) => { - return { token_identifier: token.token.identifier, token_nonce: token.token.nonce, amount: token.amount }; - }); - - const functionCallParts = Buffer.from(transaction.data).toString().split(ARGUMENTS_SEPARATOR); - const functionName = functionCallParts[0]; - const functionArguments = []; - for (let index = 1; index < functionCallParts.length; index++) { - const element = functionCallParts[index]; - functionArguments.push(element.valueOf()); - } - const functionCall = [new BytesValue(Buffer.from(utf8ToHex(functionName))), ...functionArguments]; - return new ProposeTransferExecuteEsdtInputForContract({ - multisigContract: options.multisig, - to: options.to, - functionCall: functionCall, - gasLimit: options.optGasLimit, - tokens: tokens, - }); - } -} - -export class ProposeTransferExecutInput { - multisigContract: Address; - to: Address; - gasLimit?: bigint; - functionCall: any[]; - - constructor(options: { multisigContract: Address; to: Address; gasLimit?: bigint; functionCall: any[] }) { - this.multisigContract = options.multisigContract; - this.to = options.to; - this.gasLimit = options.gasLimit; - this.functionCall = options.functionCall; - } - - static newFromTransferExecuteInput(options: { - multisig: Address; - to: Address; - tokenTransfers: TokenTransfer[]; - functionName: string; - arguments: any[]; - optGasLimit?: bigint; - abi?: Abi; - }): ProposeTransferExecutInput { - const transactionsFactory = new SmartContractTransactionsFactory({ - config: new TransactionsFactoryConfig({ chainID: "" }), - abi: options.abi, - }); - const transaction = transactionsFactory.createTransactionForExecute(Address.empty(), { - contract: Address.empty(), - function: options.functionName, - gasLimit: 0n, - arguments: options.arguments, - nativeTransferAmount: 0n, - tokenTransfers: options.tokenTransfers, - }); - const functionCallParts = Buffer.from(transaction.data).toString().split(ARGUMENTS_SEPARATOR); - const functionName = functionCallParts[0]; - const functionArguments = []; - for (let index = 1; index < functionCallParts.length; index++) { - const element = functionCallParts[index]; - functionArguments.push(element.valueOf()); - } - const functionCall = [new BytesValue(Buffer.from(utf8ToHex(functionName))), ...functionArguments]; - return new ProposeTransferExecutInput({ - multisigContract: options.multisig, - to: options.to, - functionCall: functionCall, - gasLimit: options.optGasLimit, - }); - } -} - -export class ProposeAsyncCallInput { +export type ProposeAsyncCallInput = MultisigContractInput & { multisigContract: Address; to: Address; nativeTransferAmount: bigint; @@ -200,26 +69,7 @@ export class ProposeAsyncCallInput { functionArguments: any[]; gasLimit: bigint; abi?: Abi; - constructor(options: { - multisigContract: Address; - to: Address; - nativeTransferAmount: bigint; - tokenTransfers: TokenTransfer[]; - functionName: string; - functionArguments: any[]; - gasLimit: bigint; - abi?: Abi; - }) { - this.multisigContract = options.multisigContract; - this.to = options.to; - this.nativeTransferAmount = options.nativeTransferAmount; - this.tokenTransfers = options.tokenTransfers; - this.functionName = options.functionName; - this.functionArguments = options.functionArguments; - this.gasLimit = options.gasLimit; - this.abi = options.abi; - } -} +}; export type ProposeSCDeployFromSourceInput = MultisigContractInput & { amount: bigint; From dbd39448f4cdc874dd72a44f9698531d8b4b0572 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 15 Apr 2025 15:29:46 +0300 Subject: [PATCH 06/14] Bump version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 12a2eb439..cf06e4119 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@multiversx/sdk-core", - "version": "14.0.2", + "version": "14.1.0-beta.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@multiversx/sdk-core", - "version": "14.0.2", + "version": "14.1.0-beta.0", "license": "MIT", "dependencies": { "@multiversx/sdk-transaction-decoder": "1.0.2", diff --git a/package.json b/package.json index 1e5d2d8b4..ddc442e9d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@multiversx/sdk-core", - "version": "14.0.2", + "version": "14.1.0-beta.0", "description": "MultiversX SDK for JavaScript and TypeScript", "author": "MultiversX", "homepage": "https://multiversx.com", From 02828b57ba30925a5847e0199f082842c4ca746e Mon Sep 17 00:00:00 2001 From: danielailie Date: Wed, 16 Apr 2025 15:22:26 +0300 Subject: [PATCH 07/14] Fix deploy transaction --- src/multisig/multisigTransactionsFactory.spec.ts | 4 +--- src/multisig/multisigTransactionsFactory.ts | 2 -- src/multisig/resources.ts | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/multisig/multisigTransactionsFactory.spec.ts b/src/multisig/multisigTransactionsFactory.spec.ts index 05f4c1e29..3309e623f 100644 --- a/src/multisig/multisigTransactionsFactory.spec.ts +++ b/src/multisig/multisigTransactionsFactory.spec.ts @@ -34,19 +34,17 @@ describe("test multisig transactions factory", function () { const boardMemberTwo = Address.newFromBech32("erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8"); const board = [boardMemberOne, boardMemberTwo]; - const amount = 1000000000000000000n; // 1 EGLD const transaction = factory.createTransactionForMultisigDeploy(senderAddress, { bytecode: bytecode.valueOf(), gasLimit: 5000000n, quorum: 2, board, - amount, }); assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu"); - assert.equal(transaction.value, amount); + assert.equal(transaction.value, 0n); assert.equal(transaction.chainID, config.chainID); assert.deepEqual( Buffer.from(transaction.data), diff --git a/src/multisig/multisigTransactionsFactory.ts b/src/multisig/multisigTransactionsFactory.ts index e564b0e55..54385e044 100644 --- a/src/multisig/multisigTransactionsFactory.ts +++ b/src/multisig/multisigTransactionsFactory.ts @@ -43,14 +43,12 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor * Creates a transaction to deploy a new multisig contract */ createTransactionForMultisigDeploy(sender: Address, options: resources.DeployMultisigContractInput): Transaction { - const nativeTransferAmount = options.amount ?? 0n; const boardAddresses: AddressValue[] = options.board.map((addr) => new AddressValue(addr)); const args = [new U32Value(options.quorum), VariadicValue.fromItems(...boardAddresses)]; return this.createTransactionForDeploy(sender, { bytecode: options.bytecode, gasLimit: options.gasLimit, - nativeTransferAmount, isUpgradeable: options.isUpgradeable, isReadable: options.isReadable, isPayable: options.isPayable, diff --git a/src/multisig/resources.ts b/src/multisig/resources.ts index e0b67be5e..d71f46736 100644 --- a/src/multisig/resources.ts +++ b/src/multisig/resources.ts @@ -6,7 +6,6 @@ import { CodeMetadata } from "../core/codeMetadata"; export type DeployMultisigContractInput = { quorum: number; board: Address[]; - amount?: bigint; bytecode: Uint8Array; isUpgradeable?: boolean; isReadable?: boolean; From 9243f444b2b7a60e50037625b70014c9c4f0b1aa Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 22 Apr 2025 12:20:59 +0300 Subject: [PATCH 08/14] Remove IAbi and use concret implementation --- src/abi/smartContract.ts | 20 ++++++++----------- src/multisig/index.ts | 2 +- src/multisig/multisigTransactionsFactory.ts | 11 +++------- ...=> proposeTransferExecuteContractInput.ts} | 0 .../smartContractTransactionsFactory.ts | 13 +++--------- 5 files changed, 15 insertions(+), 31 deletions(-) rename src/multisig/{proposeTransferExecuteContract.ts => proposeTransferExecuteContractInput.ts} (100%) diff --git a/src/abi/smartContract.ts b/src/abi/smartContract.ts index 7282430be..5c991eef7 100644 --- a/src/abi/smartContract.ts +++ b/src/abi/smartContract.ts @@ -19,14 +19,7 @@ import { } from "./interface"; import { NativeSerializer } from "./nativeSerializer"; import { Query } from "./query"; -import { EndpointDefinition, TypedValue } from "./typesystem"; - -interface IAbi { - constructorDefinition: EndpointDefinition; - - getEndpoints(): EndpointDefinition[]; - getEndpoint(name: string | ContractFunction): EndpointDefinition; -} +import { Abi, EndpointDefinition, TypedValue } from "./typesystem"; /** * * @deprecated component. Use "SmartContractTransactionsFactory" or "SmartContractController", instead. @@ -35,7 +28,7 @@ interface IAbi { */ export class SmartContract implements ISmartContract { private address: Address = Address.empty(); - private abi?: IAbi; + private abi?: Abi; /** * This object contains a function for each endpoint defined by the contract. @@ -55,7 +48,7 @@ export class SmartContract implements ISmartContract { /** * Create a SmartContract object by providing its address on the Network. */ - constructor(options: { address?: Address; abi?: IAbi } = {}) { + constructor(options: { address?: Address; abi?: Abi } = {}) { this.address = options.address || Address.empty(); this.abi = options.abi; @@ -105,13 +98,16 @@ export class SmartContract implements ISmartContract { return this.address; } - private getAbi(): IAbi { + private getAbi(): Abi { guardValueIsSet("abi", this.abi); return this.abi!; } getEndpoint(name: string | ContractFunction): EndpointDefinition { - return this.getAbi().getEndpoint(name); + if (typeof name === "string") { + return this.getAbi().getEndpoint(name); + } + return this.getAbi().getEndpoint(name.name); } /** diff --git a/src/multisig/index.ts b/src/multisig/index.ts index 5185e2f02..969a85053 100644 --- a/src/multisig/index.ts +++ b/src/multisig/index.ts @@ -1,3 +1,3 @@ export * from "./multisigTransactionsFactory"; -export * from "./proposeTransferExecuteContract"; +export * from "./ProposeTransferExecuteContractInput"; export * from "./resources"; diff --git a/src/multisig/multisigTransactionsFactory.ts b/src/multisig/multisigTransactionsFactory.ts index 54385e044..81d736349 100644 --- a/src/multisig/multisigTransactionsFactory.ts +++ b/src/multisig/multisigTransactionsFactory.ts @@ -1,4 +1,5 @@ import { + Abi, AddressValue, ArgSerializer, BigUIntValue, @@ -18,15 +19,9 @@ import { Address } from "../core/address"; import { Transaction } from "../core/transaction"; import { TransactionBuilder } from "../core/transactionBuilder"; import { SmartContractTransactionsFactory } from "../smartContracts"; -import { ProposeTransferExecuteContractInput } from "./proposeTransferExecuteContract"; +import { ProposeTransferExecuteContractInput } from "./ProposeTransferExecuteContractInput"; import * as resources from "./resources"; -interface IAbi { - constructorDefinition: EndpointDefinition; - upgradeConstructorDefinition?: EndpointDefinition; - - getEndpoint(name: string): EndpointDefinition; -} /** * Use this class to create multisig related transactions like creating a new multisig contract, * proposing actions, signing actions, and performing actions. @@ -34,7 +29,7 @@ interface IAbi { export class MultisigTransactionsFactory extends SmartContractTransactionsFactory { private readonly argSerializer: ArgSerializer; - constructor(options: { config: TransactionsFactoryConfig; abi?: IAbi }) { + constructor(options: { config: TransactionsFactoryConfig; abi?: Abi }) { super(options); this.argSerializer = new ArgSerializer(); } diff --git a/src/multisig/proposeTransferExecuteContract.ts b/src/multisig/proposeTransferExecuteContractInput.ts similarity index 100% rename from src/multisig/proposeTransferExecuteContract.ts rename to src/multisig/proposeTransferExecuteContractInput.ts diff --git a/src/smartContracts/smartContractTransactionsFactory.ts b/src/smartContracts/smartContractTransactionsFactory.ts index d66b0ecd6..5fc334322 100644 --- a/src/smartContracts/smartContractTransactionsFactory.ts +++ b/src/smartContracts/smartContractTransactionsFactory.ts @@ -1,4 +1,4 @@ -import { ArgSerializer, ContractFunction, EndpointDefinition, isTyped, NativeSerializer } from "../abi"; +import { Abi, ArgSerializer, EndpointDefinition, isTyped, NativeSerializer } from "../abi"; import { Address, CodeMetadata } from "../core"; import { CONTRACT_DEPLOY_ADDRESS_HEX, VM_TYPE_WASM_VM } from "../core/constants"; import { Err } from "../core/errors"; @@ -19,24 +19,17 @@ interface IConfig { gasLimitChangeOwnerAddress: bigint; } -interface IAbi { - constructorDefinition: EndpointDefinition; - upgradeConstructorDefinition?: EndpointDefinition; - - getEndpoint(name: string | ContractFunction): EndpointDefinition; -} - /** * Use this class to create transactions to deploy, call or upgrade a smart contract. */ export class SmartContractTransactionsFactory { protected readonly config: IConfig; - protected readonly abi?: IAbi; + protected readonly abi?: Abi; private readonly tokenComputer: TokenComputer; private readonly dataArgsBuilder: TokenTransfersDataBuilder; private readonly contractDeployAddress: Address; - constructor(options: { config: IConfig; abi?: IAbi }) { + constructor(options: { config: IConfig; abi?: Abi }) { this.config = options.config; this.abi = options.abi; this.tokenComputer = new TokenComputer(); From 2e3f9361a7748729bec238e0627e906057da1a6f Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 22 Apr 2025 12:28:28 +0300 Subject: [PATCH 09/14] Refector ProposeTransferExecuteContractInput --- .../proposeTransferExecuteContractInput.ts | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/multisig/proposeTransferExecuteContractInput.ts b/src/multisig/proposeTransferExecuteContractInput.ts index c6fcb78be..6c89b6cbc 100644 --- a/src/multisig/proposeTransferExecuteContractInput.ts +++ b/src/multisig/proposeTransferExecuteContractInput.ts @@ -1,5 +1,5 @@ import { Abi, BytesValue } from "../abi"; -import { Address, TokenTransfer, TransactionsFactoryConfig } from "../core"; +import { Address, TokenTransfer, Transaction, TransactionsFactoryConfig } from "../core"; import { ARGUMENTS_SEPARATOR } from "../core/constants"; import { utf8ToHex } from "../core/utils.codec"; import { SmartContractTransactionsFactory } from "../smartContracts"; @@ -36,14 +36,9 @@ export class ProposeTransferExecuteContractInput { arguments: options.arguments, nativeTransferAmount: 0n, }); - const functionCallParts = Buffer.from(transaction.data).toString().split(ARGUMENTS_SEPARATOR); - const functionName = functionCallParts[0]; - const functionArguments = []; - for (let index = 1; index < functionCallParts.length; index++) { - const element = functionCallParts[index]; - functionArguments.push(element.valueOf()); - } - const functionCall = [new BytesValue(Buffer.from(utf8ToHex(functionName))), ...functionArguments]; + + const functionCall = ProposeTransferExecuteContractInput.getFunctionCall(transaction); + return new ProposeTransferExecuteContractInput({ multisigContract: options.multisig, to: options.to, @@ -72,14 +67,9 @@ export class ProposeTransferExecuteContractInput { arguments: options.arguments, nativeTransferAmount: 0n, }); - const functionCallParts = Buffer.from(transaction.data).toString().split(ARGUMENTS_SEPARATOR); - const functionName = functionCallParts[0]; - const functionArguments = []; - for (let index = 1; index < functionCallParts.length; index++) { - const element = functionCallParts[index]; - functionArguments.push(element.valueOf()); - } - const functionCall = [new BytesValue(Buffer.from(utf8ToHex(functionName))), ...functionArguments]; + + const functionCall = ProposeTransferExecuteContractInput.getFunctionCall(transaction); + return new ProposeTransferExecuteContractInput({ multisigContract: options.multisig, to: options.to, @@ -87,4 +77,12 @@ export class ProposeTransferExecuteContractInput { gasLimit: options.optGasLimit, }); } + + private static getFunctionCall(transaction: Transaction) { + const functionCallParts = Buffer.from(transaction.data).toString().split(ARGUMENTS_SEPARATOR); + const functionName = functionCallParts[0]; + const functionArguments = functionCallParts.slice(1).map((part) => part.valueOf()); + const functionCall = [new BytesValue(Buffer.from(utf8ToHex(functionName))), ...functionArguments]; + return functionCall; + } } From 18e42ece4c79a1bfe55aea0d58076968445cd33c Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 22 Apr 2025 12:30:58 +0300 Subject: [PATCH 10/14] Extract mapTokenPayment --- src/multisig/multisigTransactionsFactory.ts | 28 +++++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/multisig/multisigTransactionsFactory.ts b/src/multisig/multisigTransactionsFactory.ts index 81d736349..55326e75f 100644 --- a/src/multisig/multisigTransactionsFactory.ts +++ b/src/multisig/multisigTransactionsFactory.ts @@ -194,17 +194,8 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor arguments: options.functionArguments, abi: options.abi, }); - const tokenComputer = new TokenComputer(); - const argsTyped = []; - for (const token of options.tokens) { - argsTyped.push({ - token_identifier: new TokenIdentifierValue( - tokenComputer.extractIdentifierFromExtendedIdentifier(token.token.identifier), - ), - token_nonce: new U64Value(token.token.nonce), - amount: new BigUIntValue(token.amount), - }); - } + + const argsTyped = this.mapTokenPayment(options); const dataParts = [ "proposeTransferExecuteEsdt", ...this.argSerializer.valuesToStrings( @@ -225,6 +216,21 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor }).build(); } + private mapTokenPayment(options: resources.ProposeTransferExecuteEsdtInput) { + const tokenComputer = new TokenComputer(); + const argsTyped = []; + for (const token of options.tokens) { + argsTyped.push({ + token_identifier: new TokenIdentifierValue( + tokenComputer.extractIdentifierFromExtendedIdentifier(token.token.identifier), + ), + token_nonce: new U64Value(token.token.nonce), + amount: new BigUIntValue(token.amount), + }); + } + return argsTyped; + } + /** * Proposes an async call to another contract */ From 8a4ea680cd976498decb5435dfa6491cade8a8ee Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 22 Apr 2025 16:08:39 +0300 Subject: [PATCH 11/14] Fix build --- src/multisig/index.ts | 2 +- src/multisig/multisigTransactionsFactory.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/multisig/index.ts b/src/multisig/index.ts index 969a85053..7af23fc96 100644 --- a/src/multisig/index.ts +++ b/src/multisig/index.ts @@ -1,3 +1,3 @@ export * from "./multisigTransactionsFactory"; -export * from "./ProposeTransferExecuteContractInput"; +export * from "./proposeTransferExecuteContractInput"; export * from "./resources"; diff --git a/src/multisig/multisigTransactionsFactory.ts b/src/multisig/multisigTransactionsFactory.ts index 55326e75f..65d837abe 100644 --- a/src/multisig/multisigTransactionsFactory.ts +++ b/src/multisig/multisigTransactionsFactory.ts @@ -7,7 +7,7 @@ import { EndpointModifiers, NativeSerializer, OptionType, - OptionValue, + OptionValue,ßß TokenIdentifierValue, U32Value, U64Type, @@ -19,7 +19,7 @@ import { Address } from "../core/address"; import { Transaction } from "../core/transaction"; import { TransactionBuilder } from "../core/transactionBuilder"; import { SmartContractTransactionsFactory } from "../smartContracts"; -import { ProposeTransferExecuteContractInput } from "./ProposeTransferExecuteContractInput"; +import { ProposeTransferExecuteContractInput } from "./proposeTransferExecuteContractInput"; import * as resources from "./resources"; /** From 6c07b4cfbe0c2dd565dd55b5d7949d8d41d48571 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 22 Apr 2025 16:09:26 +0300 Subject: [PATCH 12/14] Remove extra characters --- src/multisig/multisigTransactionsFactory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/multisig/multisigTransactionsFactory.ts b/src/multisig/multisigTransactionsFactory.ts index 65d837abe..9c22464d7 100644 --- a/src/multisig/multisigTransactionsFactory.ts +++ b/src/multisig/multisigTransactionsFactory.ts @@ -7,7 +7,7 @@ import { EndpointModifiers, NativeSerializer, OptionType, - OptionValue,ßß + OptionValue, TokenIdentifierValue, U32Value, U64Type, From e74c7ed3c20d90a36866a552b4fb6a2786b6dfe3 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 29 Apr 2025 11:34:34 +0300 Subject: [PATCH 13/14] Update map tokenPayments --- .../multisigTransactionsFactory.spec.ts | 18 ---------------- src/multisig/multisigTransactionsFactory.ts | 21 ++++++++----------- 2 files changed, 9 insertions(+), 30 deletions(-) diff --git a/src/multisig/multisigTransactionsFactory.spec.ts b/src/multisig/multisigTransactionsFactory.spec.ts index 3309e623f..73827a6bf 100644 --- a/src/multisig/multisigTransactionsFactory.spec.ts +++ b/src/multisig/multisigTransactionsFactory.spec.ts @@ -70,7 +70,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.equal(transaction.chainID, config.chainID); assert.deepEqual( transaction.data.toString(), @@ -165,7 +164,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.equal(transaction.chainID, config.chainID); assert.deepEqual( transaction.data.toString(), @@ -199,7 +197,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.equal(transaction.chainID, config.chainID); assert.deepEqual( transaction.data.toString(), @@ -229,7 +226,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.equal(transaction.chainID, config.chainID); assert.equal( transaction.data.toString(), @@ -254,7 +250,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.equal(transaction.chainID, config.chainID); assert.equal(transaction.value, 1n); assert.deepEqual(transaction.data.toString(), "deposit"); @@ -281,7 +276,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.equal(transaction.chainID, config.chainID); assert.equal(transaction.value, 0n); assert.deepEqual(transaction.data.toString(), "ESDTTransfer@414c4943452d353632376631@64@6465706f736974"); @@ -308,7 +302,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.equal(transaction.chainID, config.chainID); assert.deepEqual( transaction.data.toString(), @@ -338,7 +331,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.equal(transaction.chainID, config.chainID); assert.deepEqual( transaction.data.toString(), @@ -361,8 +353,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); - assert.equal(transaction.chainID, config.chainID); assert.deepEqual(transaction.data.toString(), "sign@2a"); }); @@ -382,7 +372,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.equal(transaction.chainID, config.chainID); assert.deepEqual(transaction.data.toString(), "signBatch@05"); }); @@ -402,8 +391,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); - assert.equal(transaction.chainID, config.chainID); assert.deepEqual(transaction.data.toString(), "signAndPerform@2a"); }); @@ -423,7 +410,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.deepEqual(transaction.data.toString(), "unsign@2a"); }); @@ -442,7 +428,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.deepEqual(transaction.data.toString(), "unsignForOutdatedBoardMembers@2a@01@03@05"); }); @@ -460,7 +445,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.deepEqual(transaction.data.toString(), "performAction@2a"); }); @@ -478,7 +462,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.deepEqual(transaction.data.toString(), "performBatch@05"); }); @@ -497,7 +480,6 @@ describe("test multisig transactions factory", function () { assert.instanceOf(transaction, Transaction); assert.equal(transaction.sender.toBech32(), senderAddress.toBech32()); assert.equal(transaction.receiver.toBech32(), multisigContractAddress.toBech32()); - assert.isAbove(transaction.data.length, 0); assert.equal(transaction.chainID, config.chainID); assert.deepEqual(transaction.data.toString(), "discardAction@0142"); }); diff --git a/src/multisig/multisigTransactionsFactory.ts b/src/multisig/multisigTransactionsFactory.ts index 9c22464d7..d0870a12b 100644 --- a/src/multisig/multisigTransactionsFactory.ts +++ b/src/multisig/multisigTransactionsFactory.ts @@ -8,7 +8,6 @@ import { NativeSerializer, OptionType, OptionValue, - TokenIdentifierValue, U32Value, U64Type, U64Value, @@ -195,12 +194,12 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor abi: options.abi, }); - const argsTyped = this.mapTokenPayment(options); + const tokenPayments: resources.EsdtTokenPayment[] = this.mapTokenPayments(options); const dataParts = [ "proposeTransferExecuteEsdt", ...this.argSerializer.valuesToStrings( NativeSerializer.nativeToTypedValues( - [options.to, argsTyped, options.gasLimit, VariadicValue.fromItems(...input.functionCall)], + [options.to, tokenPayments, options.gasLimit, VariadicValue.fromItems(...input.functionCall)], this.abi?.getEndpoint("proposeTransferExecuteEsdt") ?? new EndpointDefinition("proposeTransferExecuteEsdt", [], [], new EndpointModifiers("", [])), ), @@ -216,19 +215,17 @@ export class MultisigTransactionsFactory extends SmartContractTransactionsFactor }).build(); } - private mapTokenPayment(options: resources.ProposeTransferExecuteEsdtInput) { + private mapTokenPayments(options: resources.ProposeTransferExecuteEsdtInput): resources.EsdtTokenPayment[] { const tokenComputer = new TokenComputer(); - const argsTyped = []; + const tokens = []; for (const token of options.tokens) { - argsTyped.push({ - token_identifier: new TokenIdentifierValue( - tokenComputer.extractIdentifierFromExtendedIdentifier(token.token.identifier), - ), - token_nonce: new U64Value(token.token.nonce), - amount: new BigUIntValue(token.amount), + tokens.push({ + token_identifier: tokenComputer.extractIdentifierFromExtendedIdentifier(token.token.identifier), + token_nonce: token.token.nonce, + amount: token.amount, }); } - return argsTyped; + return tokens; } /** From 721eb9d80c89b22515a2b512b1406fc08d049af4 Mon Sep 17 00:00:00 2001 From: danielailie Date: Tue, 29 Apr 2025 15:21:17 +0300 Subject: [PATCH 14/14] Increase sleep for integration tests --- .github/workflows/test-localnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-localnet.yml b/.github/workflows/test-localnet.yml index d324b54d1..61cdae4c0 100644 --- a/.github/workflows/test-localnet.yml +++ b/.github/workflows/test-localnet.yml @@ -42,7 +42,7 @@ jobs: mkdir -p ~/localnet && cd ~/localnet mxpy localnet setup --configfile=${GITHUB_WORKSPACE}/localnet.toml nohup mxpy localnet start --configfile=${GITHUB_WORKSPACE}/localnet.toml > localnet.log 2>&1 & echo $! > localnet.pid - sleep 10 # Allow time for the testnet to fully start + sleep 120 # Allow time for the testnet to fully start # Step 6: Install Node.js and dependencies - name: Set up Node.js environment