From 9449b999f07adaa930e1bff7d8b8c90bac790790 Mon Sep 17 00:00:00 2001 From: cdc-Hitesh Date: Mon, 14 Jun 2021 13:18:05 +0530 Subject: [PATCH 1/8] - Support Multiple Fee Amounts - AuthInfo changed --- lib/src/cosmos/v1beta1/types/tx.ts | 2 +- lib/src/transaction/raw.ts | 28 ++++++++++++++++++---- lib/src/transaction/signable.ts | 27 +++++++-------------- lib/src/utils/protoBuf/encoder/authInfo.ts | 5 +++- 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/lib/src/cosmos/v1beta1/types/tx.ts b/lib/src/cosmos/v1beta1/types/tx.ts index b54adacb..dc4e065e 100644 --- a/lib/src/cosmos/v1beta1/types/tx.ts +++ b/lib/src/cosmos/v1beta1/types/tx.ts @@ -18,7 +18,7 @@ export type TxBody = { export type AuthInfo = { signerInfos: SignerInfo[]; fee: { - amount?: ICoin; + amount?: ICoin[]; gasLimit?: Big; payer?: string; granter?: string; diff --git a/lib/src/transaction/raw.ts b/lib/src/transaction/raw.ts index 3bb9fd76..cf0a635b 100644 --- a/lib/src/transaction/raw.ts +++ b/lib/src/transaction/raw.ts @@ -160,19 +160,37 @@ export const rawTransaction = function (config: InitConfigurations) { } /** - * Set fee to the raw tx - * @param {ICoin} fee to be set to the raw tx body + * Sets a single fee amount to the raw tx + * @param {ICoin} feeAmount amount to be set to the raw tx body * @returns {RawTransaction} * @throws {Error} when fee set is invalid * @memberof Transaction + * @deprecated */ - public setFee(fee: ICoin): RawTransaction { - ow(fee, 'fee', owCoin()); - this.authInfo.fee.amount = fee; + public setFee(feeAmount: ICoin): RawTransaction { + ow(feeAmount, 'fee', owCoin()); + this.authInfo.fee.amount = [feeAmount]; return this; } + /** + * Appends a + * @param {ICoin} feeAmount to be set to the raw tx body + * @returns {RawTransaction} + * @throws {Error} when fee set is invalid + * @memberof Transaction + * @deprecated + */ + public appendFeeAmount(feeAmount: ICoin): RawTransaction { + ow(feeAmount, 'feeAmount', owCoin()); + if (typeof this.authInfo.fee.amount === 'undefined') { + this.authInfo.fee.amount = []; + } + this.authInfo.fee.amount.push(feeAmount); + return this; + } + /** * Set a timeout param to tx body * @param {string} timeoutHeight to best to the broad-casted tx diff --git a/lib/src/transaction/signable.ts b/lib/src/transaction/signable.ts index 909a8a47..2b3dafda 100644 --- a/lib/src/transaction/signable.ts +++ b/lib/src/transaction/signable.ts @@ -145,28 +145,19 @@ export class SignableTransaction { }); }); - if (cosmosAuthInfo.fee.amount.length > 1) { - // TODO: Multi-coin support - throw new Error(`More than one fee amount in transaction is not supported`); - } - - let feeAmount; - let feeAmountCoin; - // Todo: handle multiple fee amounts - if (cosmosAuthInfo.fee.amount.length === 1) { - [feeAmount] = cosmosAuthInfo.fee.amount; - } + const feeAmountList: ICoin[] = []; - if (feeAmount) { - const feeAmountString = feeAmount.amount!; + cosmosAuthInfo.fee.amount.forEach((feeAmount) => { + const feeAmountString = feeAmount.amount; const feeAmountDenom = feeAmount.denom; - feeAmountCoin = croSdk.Coin.fromCustomAmountDenom(feeAmountString, feeAmountDenom); - } + const feeAmountCoin = croSdk.Coin.fromCustomAmountDenom(feeAmountString, feeAmountDenom); + feeAmountList.push(feeAmountCoin); + }); const authInfo: AuthInfo = { signerInfos, fee: { - amount: feeAmountCoin || undefined, + amount: feeAmountList || undefined, gasLimit: new Big(cosmosAuthInfo.fee.gas_limit || DEFAULT_GAS_LIMIT), payer: cosmosAuthInfo.fee.payer, granter: cosmosAuthInfo.fee.granter, @@ -435,9 +426,9 @@ const legacyEncodeMsgs = (msgs: CosmosMsg[]): legacyAmino.Msg[] => { return msgs.map((msg) => msg.toRawAminoMsg()); }; -const legacyEncodeStdFee = (fee: ICoin | undefined, gas: Big | undefined): legacyAmino.StdFee => { +const legacyEncodeStdFee = (feeAmountList: ICoin[] | undefined, gas: Big | undefined): legacyAmino.StdFee => { return { - amount: fee ? fee.toCosmosCoins() : [], + amount: feeAmountList ? feeAmountList.map((feeAmount) => feeAmount.toCosmosCoin()) : [], gas: gas ? gas.toString() : DEFAULT_GAS_LIMIT.toString(), }; }; diff --git a/lib/src/utils/protoBuf/encoder/authInfo.ts b/lib/src/utils/protoBuf/encoder/authInfo.ts index 5fe6d36b..35f37dd4 100644 --- a/lib/src/utils/protoBuf/encoder/authInfo.ts +++ b/lib/src/utils/protoBuf/encoder/authInfo.ts @@ -17,7 +17,10 @@ export const protoEncodeAuthInfo = (authInfo: AuthInfo): Bytes => { }), ), fee: { - amount: authInfo.fee.amount !== undefined ? [authInfo.fee.amount.toCosmosCoin()] : [], + amount: + authInfo.fee.amount !== undefined + ? authInfo.fee.amount.map((feeAmount) => feeAmount.toCosmosCoin()) + : [], gasLimit: protoEncodeGasLimitOrDefault(authInfo), }, }; From 6eb809f87bb8f342ba5eb808133e16abd8304618 Mon Sep 17 00:00:00 2001 From: cdc-Hitesh Date: Tue, 15 Jun 2021 12:45:57 +0530 Subject: [PATCH 2/8] Added structure integrity checks #274 --- lib/src/transaction/signable.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/src/transaction/signable.ts b/lib/src/transaction/signable.ts index 2b3dafda..4dee829a 100644 --- a/lib/src/transaction/signable.ts +++ b/lib/src/transaction/signable.ts @@ -109,7 +109,9 @@ export class SignableTransaction { txBody.value.messages.push(nativeMsg); }); - // TODO: structure integrity check + if (typeof cosmosObj.auth_info === 'undefined') { + throw new Error('Decoded Tx does not have a valid `authInfo`'); + } const cosmosAuthInfo = cosmosObj.auth_info; const cosmosSignerInfos = cosmosAuthInfo.signer_infos; const signerInfos: SignerInfo[] = []; @@ -147,6 +149,10 @@ export class SignableTransaction { const feeAmountList: ICoin[] = []; + if (typeof cosmosAuthInfo.fee === 'undefined' || typeof cosmosAuthInfo.fee.amount === 'undefined') { + throw new Error('Decoded Tx AuthInfo does not have a valid `fee`'); + } + cosmosAuthInfo.fee.amount.forEach((feeAmount) => { const feeAmountString = feeAmount.amount; const feeAmountDenom = feeAmount.denom; From 489f127949044d49e5062910c4f42003634290e8 Mon Sep 17 00:00:00 2001 From: cdc-Hitesh Date: Mon, 21 Jun 2021 13:06:51 +0530 Subject: [PATCH 3/8] #272: Add unit test --- lib/src/transaction/raw.spec.ts | 27 +++++++++++++++++++++++++++ lib/src/transaction/raw.ts | 4 ++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/src/transaction/raw.spec.ts b/lib/src/transaction/raw.spec.ts index 30490682..dc3200bd 100644 --- a/lib/src/transaction/raw.spec.ts +++ b/lib/src/transaction/raw.spec.ts @@ -77,6 +77,33 @@ describe('Transaction', function () { expect(actualSignerInfos[1].sequence).to.deep.eq(anotherSigner.accountSequence); }); + it('should set a single fee `amount` to AuthInfo', function () { + const tx = anyTransaction(); + tx.setFee(cro.Coin.fromBaseUnit('10000')); + + expect(tx.getAuthInfo().fee.amount).to.have.length(1); + expect(tx.getAuthInfo().fee!.amount![0].toCosmosCoin()).to.deep.eq({ + amount: '10000', + denom: 'basetcro', + }); + }); + + it('should append fee `amount` to AuthInfo', function () { + const tx = anyTransaction(); + tx.appendFeeAmount(cro.Coin.fromBaseUnit('88888')); + tx.appendFeeAmount(cro.Coin.fromBaseUnit('99999')); + + expect(tx.getAuthInfo().fee.amount).to.have.length(2); + expect(tx.getAuthInfo().fee!.amount![0].toCosmosCoin()).to.deep.eq({ + amount: '88888', + denom: 'basetcro', + }); + expect(tx.getAuthInfo().fee!.amount![1].toCosmosCoin()).to.deep.eq({ + amount: '99999', + denom: 'basetcro', + }); + }); + it('should append signer to signerAccountNumbers', function () { const anySigner = TransactionSignerFactory.build(); diff --git a/lib/src/transaction/raw.ts b/lib/src/transaction/raw.ts index cf0a635b..96c50cc1 100644 --- a/lib/src/transaction/raw.ts +++ b/lib/src/transaction/raw.ts @@ -160,7 +160,7 @@ export const rawTransaction = function (config: InitConfigurations) { } /** - * Sets a single fee amount to the raw tx + * Sets a `single` only fee amount to the raw tx * @param {ICoin} feeAmount amount to be set to the raw tx body * @returns {RawTransaction} * @throws {Error} when fee set is invalid @@ -175,7 +175,7 @@ export const rawTransaction = function (config: InitConfigurations) { } /** - * Appends a + * Appends an `Amount` to the AuthInfo Fee Amount List * @param {ICoin} feeAmount to be set to the raw tx body * @returns {RawTransaction} * @throws {Error} when fee set is invalid From b4cd088796b17db986ff126ab92b71a3bc0317fa Mon Sep 17 00:00:00 2001 From: cdc-Hitesh Date: Mon, 21 Jun 2021 13:09:48 +0530 Subject: [PATCH 4/8] #272: Integration test with `.setFee` --- lib/e2e/transaction.spec.ts | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/lib/e2e/transaction.spec.ts b/lib/e2e/transaction.spec.ts index c1404d19..dfdb3ca5 100644 --- a/lib/e2e/transaction.spec.ts +++ b/lib/e2e/transaction.spec.ts @@ -189,6 +189,65 @@ describe('e2e test suite', function () { const { transactionHash } = broadcastResult; expect(transactionHash).to.match(/^[0-9A-F]{64}$/); }); + it('[BANK] creates a MsgSend Type Transaction with `Fee` amount and Broadcasts it.', async function () { + const hdKey = HDKey.fromMnemonic(env.mnemonic.communityAccount); + const hdKey2 = HDKey.fromMnemonic(env.mnemonic.reserveAccount); + const hdKey3 = HDKey.fromMnemonic(env.mnemonic.randomEmptyAccount); + const privKey = hdKey.derivePrivKey(`m/44'/${customNetwork.bip44Path.coinType}'/0'/0/0`); + const privKey2 = hdKey2.derivePrivKey(`m/44'/${customNetwork.bip44Path.coinType}'/0'/0/0`); + const randomPrivKey = hdKey3.derivePrivKey(`m/44'/${customNetwork.bip44Path.coinType}'/0'/0/0`); + const keyPair = Secp256k1KeyPair.fromPrivKey(privKey); + const keyPair2 = Secp256k1KeyPair.fromPrivKey(privKey2); + const randomKeyPair = Secp256k1KeyPair.fromPrivKey(randomPrivKey); + + const cro = CroSDK({ network: customNetwork }); + const rawTx = new cro.RawTransaction(); + const address1 = new cro.Address(keyPair.getPubKey()); + const address2 = new cro.Address(keyPair2.getPubKey()); + const randomAddress = new cro.Address(randomKeyPair.getPubKey()); + const client = await cro.CroClient.connect(); + + const msgSend1 = new cro.bank.MsgSend({ + fromAddress: address1.account(), + toAddress: randomAddress.account(), + amount: new cro.Coin('100000', Units.BASE), + }); + + const msgSend2 = new cro.bank.MsgSend({ + fromAddress: address2.account(), + toAddress: address1.account(), + amount: new cro.Coin('20000', Units.BASE), + }); + + const account1 = await client.getAccount(address1.account()); + const account2 = await client.getAccount(address2.account()); + + expect(account1).to.be.not.null; + expect(account2).to.be.not.null; + + const signableTx = rawTx + .appendMessage(msgSend1) + .addSigner({ + publicKey: keyPair.getPubKey(), + accountNumber: new Big(account1!.accountNumber), + accountSequence: new Big(account1!.sequence), + }) + .setFee(cro.Coin.fromCRO("0.002")) + .toSignable(); + + const signedTx = signableTx + .setSignature(0, keyPair.sign(signableTx.toSignDocumentHash(0))) + .setSignature(1, keyPair2.sign(signableTx.toSignDocumentHash(1))) + .toSigned(); + + expect(msgSend1.fromAddress).to.eq(account1!.address); + expect(msgSend1.toAddress).to.eq(randomAddress.account()); + const broadcastResult = await client.broadcastTx(signedTx.encode().toUint8Array()); + assertIsBroadcastTxSuccess(broadcastResult); + + const { transactionHash } = broadcastResult; + expect(transactionHash).to.match(/^[0-9A-F]{64}$/); + }); it('[STAKING] Creates, signs and broadcasts a `MsgDelegate` Tx', async function () { const hdKey = HDKey.fromMnemonic(env.mnemonic.ecosystemAccount); From 433d44c650f6941d03689e063e954c3bd09bda0c Mon Sep 17 00:00:00 2001 From: cdc-Hitesh Date: Mon, 21 Jun 2021 13:28:06 +0530 Subject: [PATCH 5/8] #272: Eslint --- lib/e2e/transaction.spec.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/e2e/transaction.spec.ts b/lib/e2e/transaction.spec.ts index dfdb3ca5..c8ba6161 100644 --- a/lib/e2e/transaction.spec.ts +++ b/lib/e2e/transaction.spec.ts @@ -1,3 +1,4 @@ +// @ts-nocheck /* eslint-disable */ import 'mocha'; import Big from 'big.js'; @@ -213,12 +214,6 @@ describe('e2e test suite', function () { amount: new cro.Coin('100000', Units.BASE), }); - const msgSend2 = new cro.bank.MsgSend({ - fromAddress: address2.account(), - toAddress: address1.account(), - amount: new cro.Coin('20000', Units.BASE), - }); - const account1 = await client.getAccount(address1.account()); const account2 = await client.getAccount(address2.account()); From fc0bab652a47779eef92dd1d3835bc150dba3c39 Mon Sep 17 00:00:00 2001 From: cdc-Hitesh Date: Mon, 21 Jun 2021 17:04:37 +0530 Subject: [PATCH 6/8] #272: Fix Integration test --- lib/e2e/transaction.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/e2e/transaction.spec.ts b/lib/e2e/transaction.spec.ts index c8ba6161..b14b1f1a 100644 --- a/lib/e2e/transaction.spec.ts +++ b/lib/e2e/transaction.spec.ts @@ -232,7 +232,6 @@ describe('e2e test suite', function () { const signedTx = signableTx .setSignature(0, keyPair.sign(signableTx.toSignDocumentHash(0))) - .setSignature(1, keyPair2.sign(signableTx.toSignDocumentHash(1))) .toSigned(); expect(msgSend1.fromAddress).to.eq(account1!.address); From 184f865eb307f27b05d42ce16b4e24ad8a72b7f6 Mon Sep 17 00:00:00 2001 From: cdc-Hitesh Date: Wed, 23 Jun 2021 16:16:05 +0530 Subject: [PATCH 7/8] #272 Feedback change .forEach to .map --- lib/src/transaction/signable.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/src/transaction/signable.ts b/lib/src/transaction/signable.ts index 4dee829a..57cc28d8 100644 --- a/lib/src/transaction/signable.ts +++ b/lib/src/transaction/signable.ts @@ -147,17 +147,15 @@ export class SignableTransaction { }); }); - const feeAmountList: ICoin[] = []; - if (typeof cosmosAuthInfo.fee === 'undefined' || typeof cosmosAuthInfo.fee.amount === 'undefined') { throw new Error('Decoded Tx AuthInfo does not have a valid `fee`'); } - cosmosAuthInfo.fee.amount.forEach((feeAmount) => { + const feeAmountList: ICoin[] = cosmosAuthInfo.fee.amount.map((feeAmount) => { const feeAmountString = feeAmount.amount; const feeAmountDenom = feeAmount.denom; const feeAmountCoin = croSdk.Coin.fromCustomAmountDenom(feeAmountString, feeAmountDenom); - feeAmountList.push(feeAmountCoin); + return feeAmountCoin; }); const authInfo: AuthInfo = { From 67ab8f17a57ac50a6063c58b7eb10f3ba3ce1973 Mon Sep 17 00:00:00 2001 From: cdc-Hitesh Date: Wed, 23 Jun 2021 16:29:57 +0530 Subject: [PATCH 8/8] #272: Typo fix --- lib/src/transaction/raw.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/transaction/raw.ts b/lib/src/transaction/raw.ts index 96c50cc1..84eed5af 100644 --- a/lib/src/transaction/raw.ts +++ b/lib/src/transaction/raw.ts @@ -180,7 +180,6 @@ export const rawTransaction = function (config: InitConfigurations) { * @returns {RawTransaction} * @throws {Error} when fee set is invalid * @memberof Transaction - * @deprecated */ public appendFeeAmount(feeAmount: ICoin): RawTransaction { ow(feeAmount, 'feeAmount', owCoin());