diff --git a/packages/web3/src/contract/contract.ts b/packages/web3/src/contract/contract.ts index c6291d61c..9d90eec8b 100644 --- a/packages/web3/src/contract/contract.ts +++ b/packages/web3/src/contract/contract.ts @@ -578,7 +578,6 @@ export class Contract extends Artifact { const selectedAccount = await signer.getSelectedAccount() const signerParams: SignDeployContractChainedTxParams = { type: 'DeployContract', - publicKey: selectedAccount.publicKey, signerAddress: selectedAccount.address, signerKeyType: selectedAccount.keyType, bytecode: bytecode, @@ -760,7 +759,6 @@ export class Script extends Artifact { const selectedAccount = await signer.getSelectedAccount() const signerParams: SignExecuteScriptChainedTxParams = { type: 'ExecuteScript', - publicKey: selectedAccount.publicKey, signerAddress: selectedAccount.address, signerKeyType: selectedAccount.keyType, bytecode: this.buildByteCodeToDeploy(params.initialFields ?? {}), diff --git a/packages/web3/src/signer/tx-builder.ts b/packages/web3/src/signer/tx-builder.ts index b703b4754..f0d9d2d6f 100644 --- a/packages/web3/src/signer/tx-builder.ts +++ b/packages/web3/src/signer/tx-builder.ts @@ -70,7 +70,7 @@ export abstract class TransactionBuilder { ): Promise> { const data = this.buildTransferTxParams(params, publicKey) const response = await this.nodeProvider.transactions.postTransactionsBuild(data) - return { ...response, gasPrice: fromApiNumber256(response.gasPrice) } + return this.convertTransferTxResult(response) } async buildDeployContractTx( @@ -79,8 +79,7 @@ export abstract class TransactionBuilder { ): Promise> { const data = this.buildDeployContractTxParams(params, publicKey) const response = await this.nodeProvider.contracts.postContractsUnsignedTxDeployContract(data) - const contractId = binToHex(contractIdFromAddress(response.contractAddress)) - return { ...response, groupIndex: response.fromGroup, contractId, gasPrice: fromApiNumber256(response.gasPrice) } + return this.convertDeployContractTxResult(response) } async buildExecuteScriptTx( @@ -89,22 +88,28 @@ export abstract class TransactionBuilder { ): Promise> { const data = this.buildExecuteScriptTxParams(params, publicKey) const response = await this.nodeProvider.contracts.postContractsUnsignedTxExecuteScript(data) - return { ...response, groupIndex: response.fromGroup, gasPrice: fromApiNumber256(response.gasPrice) } + return this.convertExecuteScriptTxResult(response) } - async buildChainedTx(params: BuildChainedTxParams[]): Promise { - const data: BuildTransaction[] = params.map((param) => { + async buildChainedTx(params: BuildChainedTxParams[], publicKeys: string[]): Promise { + if (params.length !== publicKeys.length) { + throw new Error( + 'The number of build chained transaction parameters must match the number of public keys provided' + ) + } + + const data: BuildTransaction[] = params.map((param, index) => { switch (param.type) { case 'Transfer': { - const value = this.buildTransferTxParams(param, param.publicKey) + const value = this.buildTransferTxParams(param, publicKeys[index]) return { type: param.type, value } } case 'DeployContract': { - const value = this.buildDeployContractTxParams(param, param.publicKey) + const value = this.buildDeployContractTxParams(param, publicKeys[index]) return { type: param.type, value } } case 'ExecuteScript': { - const value = this.buildExecuteScriptTxParams(param, param.publicKey) + const value = this.buildExecuteScriptTxParams(param, publicKeys[index]) return { type: param.type, value } } default: @@ -119,28 +124,21 @@ export abstract class TransactionBuilder { case 'Transfer': { const buildTransferTxResult = buildResult.value return { - ...buildTransferTxResult, - gasPrice: fromApiNumber256(buildTransferTxResult.gasPrice), + ...this.convertTransferTxResult(buildTransferTxResult), type: buildResult.type - } as SignTransferChainedTxResult + } } case 'DeployContract': { const buildDeployContractTxResult = buildResult.value as BuildDeployContractTxResult - const contractId = binToHex(contractIdFromAddress(buildDeployContractTxResult.contractAddress)) return { - ...buildDeployContractTxResult, - groupIndex: buildDeployContractTxResult.fromGroup, - contractId, - gasPrice: fromApiNumber256(buildDeployContractTxResult.gasPrice), + ...this.convertDeployContractTxResult(buildDeployContractTxResult), type: buildResult.type } as SignDeployContractChainedTxResult } case 'ExecuteScript': { const buildExecuteScriptTxResult = buildResult.value return { - ...buildExecuteScriptTxResult, - groupIndex: buildExecuteScriptTxResult.fromGroup, - gasPrice: fromApiNumber256(buildExecuteScriptTxResult.gasPrice), + ...this.convertExecuteScriptTxResult(buildExecuteScriptTxResult), type: buildResult.type } as SignExecuteScriptChainedTxResult } @@ -211,4 +209,33 @@ export abstract class TransactionBuilder { ...rest } } + + private convertTransferTxResult(result: node.BuildTransferTxResult): Omit { + return { + ...result, + gasPrice: fromApiNumber256(result.gasPrice) + } + } + + private convertDeployContractTxResult( + result: node.BuildDeployContractTxResult + ): Omit { + const contractId = binToHex(contractIdFromAddress(result.contractAddress)) + return { + ...result, + groupIndex: result.fromGroup, + contractId, + gasPrice: fromApiNumber256(result.gasPrice) + } + } + + private convertExecuteScriptTxResult( + result: node.BuildExecuteScriptTxResult + ): Omit { + return { + ...result, + groupIndex: result.fromGroup, + gasPrice: fromApiNumber256(result.gasPrice) + } + } } diff --git a/packages/web3/src/signer/types.ts b/packages/web3/src/signer/types.ts index 6ea0ae705..d78eb5c68 100644 --- a/packages/web3/src/signer/types.ts +++ b/packages/web3/src/signer/types.ts @@ -138,12 +138,11 @@ export interface SignUnsignedTxResult { } assertType> -export type SignTransferChainedTxParams = SignTransferTxParams & { type: 'Transfer'; publicKey: string } +export type SignTransferChainedTxParams = SignTransferTxParams & { type: 'Transfer' } export type SignDeployContractChainedTxParams = SignDeployContractTxParams & { type: 'DeployContract' - publicKey: string } -export type SignExecuteScriptChainedTxParams = SignExecuteScriptTxParams & { type: 'ExecuteScript'; publicKey: string } +export type SignExecuteScriptChainedTxParams = SignExecuteScriptTxParams & { type: 'ExecuteScript' } export type BuildChainedTxParams = | SignTransferChainedTxParams | SignDeployContractChainedTxParams diff --git a/test/transaction.test.ts b/test/transaction.test.ts index b29417a9e..dfbb7afa3 100644 --- a/test/transaction.test.ts +++ b/test/transaction.test.ts @@ -114,14 +114,12 @@ describe('transactions', function () { const transferFrom1To2: SignTransferChainedTxParams = { signerAddress: signer1.address, destinations: [{ address: signer2.address, attoAlphAmount: 10n * ONE_ALPH }], - publicKey: signer1.publicKey, type: 'Transfer' } const transferFrom2To3: SignTransferChainedTxParams = { signerAddress: signer2.address, destinations: [{ address: signer3.address, attoAlphAmount: 5n * ONE_ALPH }], - publicKey: signer2.publicKey, type: 'Transfer' } @@ -130,7 +128,8 @@ describe('transactions', function () { ) const [transferFrom1To2Result, transferFrom2To3Result] = await TransactionBuilder.from(nodeProvider).buildChainedTx( - [transferFrom1To2, transferFrom2To3] + [transferFrom1To2, transferFrom2To3], + [signer1.publicKey, signer2.publicKey] ) const signedTransferFrom1To2 = await signer1.signAndSubmitUnsignedTx({ @@ -175,14 +174,13 @@ describe('transactions', function () { const transferTxParams: SignTransferChainedTxParams = { signerAddress: signer1.address, destinations: [{ address: signer2.address, attoAlphAmount: 10n * ONE_ALPH }], - type: 'Transfer', - publicKey: signer1.publicKey + type: 'Transfer' } - const [transferResult, deployResult] = await TransactionBuilder.from(nodeProvider).buildChainedTx([ - transferTxParams, - deployTxParams - ]) + const [transferResult, deployResult] = await TransactionBuilder.from(nodeProvider).buildChainedTx( + [transferTxParams, deployTxParams], + [signer1.publicKey, signer2.publicKey] + ) await signer1.signAndSubmitUnsignedTx({ unsignedTx: transferResult.unsignedTx, @@ -238,14 +236,13 @@ describe('transactions', function () { const transferTxParams: SignTransferChainedTxParams = { signerAddress: signer1.address, destinations: [{ address: signer2.address, attoAlphAmount: 10n * ONE_ALPH }], - type: 'Transfer', - publicKey: signer1.publicKey + type: 'Transfer' } - const [transferResult, depositResult] = await TransactionBuilder.from(nodeProvider).buildChainedTx([ - transferTxParams, - depositTxParams - ]) + const [transferResult, depositResult] = await TransactionBuilder.from(nodeProvider).buildChainedTx( + [transferTxParams, depositTxParams], + [signer1.publicKey, signer2.publicKey] + ) await signer1.signAndSubmitUnsignedTx({ unsignedTx: transferResult.unsignedTx, @@ -274,4 +271,45 @@ describe('transactions', function () { const contractState = await transactInstance.fetchState() expect(contractState.fields.totalALPH).toEqual(ONE_ALPH) }) + + it.only('should fail when public keys do not match the build chained transactions parameters', async () => { + const nodeProvider = web3.getCurrentNodeProvider() + const signer1 = await getSigner(100n * ONE_ALPH, 1) + const signer2 = await getSigner(0n, 2) + const signer3 = await getSigner(0n, 3) + + const transferFrom1To2: SignTransferChainedTxParams = { + signerAddress: signer1.address, + destinations: [{ address: signer2.address, attoAlphAmount: 10n * ONE_ALPH }], + type: 'Transfer' + } + + const transferFrom2To3: SignTransferChainedTxParams = { + signerAddress: signer2.address, + destinations: [{ address: signer3.address, attoAlphAmount: 5n * ONE_ALPH }], + type: 'Transfer' + } + + await expect( + TransactionBuilder.from(nodeProvider).buildChainedTx([transferFrom1To2, transferFrom2To3], [signer1.publicKey]) + ).rejects.toThrow( + 'The number of build chained transaction parameters must match the number of public keys provided' + ) + + await expect( + TransactionBuilder.from(nodeProvider).buildChainedTx( + [transferFrom1To2, transferFrom2To3], + [signer1.publicKey, signer2.publicKey, signer1.publicKey] + ) + ).rejects.toThrow( + 'The number of build chained transaction parameters must match the number of public keys provided' + ) + + await expect( + TransactionBuilder.from(nodeProvider).buildChainedTx( + [transferFrom1To2, transferFrom2To3], + [signer2.publicKey, signer1.publicKey] + ) + ).rejects.toThrow('Unmatched public key') + }) })