Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development #979

Merged
merged 5 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [[v2.24.3]](https://github.com/multiversx/mx-sdk-dapp/pull/979)] - 2023-11-29

- [Removed `senderUsername` and `receiverUsername` for `SetGuardian` tx](https://github.com/multiversx/mx-sdk-dapp/pull/978)
- [Added extra gas fee for guardian transactions](https://github.com/multiversx/mx-sdk-dapp/pull/977)

## [[v2.24.2]](https://github.com/multiversx/mx-sdk-dapp/pull/976)] - 2023-11-28
- [Fixed logout for wallet provider](https://github.com/multiversx/mx-sdk-dapp/pull/975)

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@multiversx/sdk-dapp",
"version": "2.24.2",
"version": "2.24.3",
"description": "A library to hold the main logic for a dapp on the MultiversX blockchain",
"author": "MultiversX",
"license": "GPL-3.0-or-later",
Expand Down
5 changes: 5 additions & 0 deletions src/constants/guardianActions.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum GuardianActionsEnum {
SetGuardian = 'SetGuardian',
GuardAccount = 'GuardAccount',
UnGuardAccount = 'UnGuardAccount'
}
1 change: 1 addition & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './dataTestIds.enum';
export * from './errorsMessages';
export * from './extension.constants';
export * from './guardianActions.enum';
export * from './ledger.constants';
export * from './ledgerErrorCodes';
export * from './mnemonicWords';
Expand Down
51 changes: 28 additions & 23 deletions src/models/newTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,45 @@ import {
import { GAS_LIMIT, GAS_PRICE, VERSION } from 'constants/index';
import { RawTransactionType } from 'types';
import { getDataPayloadForTransaction } from 'utils/transactions/getDataPayloadForTransaction';
import { isGuardianTx } from 'utils/transactions/isGuardianTx';

export function newTransaction(rawTransaction: RawTransactionType) {
const rawTx = Object.assign({}, rawTransaction);

// TODO: Remove when the protocol supports usernames for guardian transactions
if (isGuardianTx({ data: rawTx.data, onlySetGuardian: true })) {
delete rawTx.senderUsername;
delete rawTx.receiverUsername;
}

const transaction = new Transaction({
value: rawTransaction.value.valueOf(),
data: getDataPayloadForTransaction(rawTransaction.data),
nonce: rawTransaction.nonce.valueOf(),
receiver: new Address(rawTransaction.receiver),
...(rawTransaction.receiverUsername
? { receiverUsername: rawTransaction.receiverUsername }
: {}),
sender: new Address(rawTransaction.sender),
...(rawTransaction.senderUsername
? { senderUsername: rawTransaction.senderUsername }
value: rawTx.value.valueOf(),
data: getDataPayloadForTransaction(rawTx.data),
nonce: rawTx.nonce.valueOf(),
receiver: new Address(rawTx.receiver),
...(rawTx.receiverUsername
? { receiverUsername: rawTx.receiverUsername }
: {}),
gasLimit: rawTransaction.gasLimit.valueOf() ?? GAS_LIMIT,
gasPrice: rawTransaction.gasPrice.valueOf() ?? GAS_PRICE,
chainID: rawTransaction.chainID.valueOf(),
version: new TransactionVersion(rawTransaction.version ?? VERSION),
...(rawTransaction.options
? { options: new TransactionOptions(rawTransaction.options) }
sender: new Address(rawTx.sender),
...(rawTx.senderUsername ? { senderUsername: rawTx.senderUsername } : {}),
gasLimit: rawTx.gasLimit.valueOf() ?? GAS_LIMIT,
gasPrice: rawTx.gasPrice.valueOf() ?? GAS_PRICE,
chainID: rawTx.chainID.valueOf(),
version: new TransactionVersion(rawTx.version ?? VERSION),
...(rawTx.options
? { options: new TransactionOptions(rawTx.options) }
: {}),
...(rawTransaction.guardian
? { guardian: new Address(rawTransaction.guardian) }
: {})
...(rawTx.guardian ? { guardian: new Address(rawTx.guardian) } : {})
});

if (rawTransaction.guardianSignature) {
if (rawTx.guardianSignature) {
transaction.applyGuardianSignature(
Buffer.from(rawTransaction.guardianSignature, 'hex')
Buffer.from(rawTx.guardianSignature, 'hex')
);
}

if (rawTransaction.signature) {
transaction.applySignature(Buffer.from(rawTransaction.signature, 'hex'));
if (rawTx.signature) {
transaction.applySignature(Buffer.from(rawTx.signature, 'hex'));
}

return transaction;
Expand Down
10 changes: 9 additions & 1 deletion src/services/transactions/signTransactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
SendTransactionReturnType,
SignTransactionsPropsType
} from 'types';
import { isGuardianTx } from 'utils/transactions/isGuardianTx';
import { stringIsFloat } from 'utils/validation/stringIsFloat';
import { calcTotalFee } from './utils';

Expand Down Expand Up @@ -74,8 +75,15 @@ export async function signTransactions({
customTransactionInformation?.signWithoutSending ?? true
},
transactions: transactionsPayload.map((tx) => {
const transaction = tx.toPlainObject();

// TODO: Remove when the protocol supports usernames for guardian transactions
if (isGuardianTx({ data: transaction.data, onlySetGuardian: true })) {
return transaction;
}

return {
...tx.toPlainObject(),
...transaction,
senderUsername: tx.getSenderUsername().valueOf(),
receiverUsername: tx.getReceiverUsername().valueOf()
};
Expand Down
23 changes: 19 additions & 4 deletions src/utils/operations/calculateFeeLimit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import {
TokenPayment
} from '@multiversx/sdk-core';
import { NetworkConfig } from '@multiversx/sdk-network-providers';
import { GAS_LIMIT, GAS_PRICE, ZERO } from 'constants/index';
import BigNumber from 'bignumber.js';
import {
EXTRA_GAS_LIMIT_GUARDED_TX,
GAS_LIMIT,
GAS_PRICE,
ZERO
} from 'constants/index';
import { isGuardianTx } from 'utils/transactions/isGuardianTx';
import { stringIsFloat, stringIsInteger } from 'utils/validation';

export interface CalculateFeeLimitType {
Expand Down Expand Up @@ -35,14 +42,21 @@ export function calculateFeeLimit({
}: CalculateFeeLimitType) {
const data = inputData || '';
const validGasLimit = stringIsInteger(gasLimit) ? gasLimit : minGasLimit;

// We need to add extra gas fee for guardian transactions
const extraGasLimit = isGuardianTx({ data }) ? EXTRA_GAS_LIMIT_GUARDED_TX : 0;
const usedGasLimit = new BigNumber(validGasLimit)
.plus(extraGasLimit)
.toNumber();

const validGasPrice = stringIsFloat(gasPrice) ? gasPrice : defaultGasPrice;
const transaction = new Transaction({
nonce: 0,
value: TokenPayment.egldFromAmount('0'),
receiver: new Address(placeholderData.to),
sender: new Address(placeholderData.to),
gasPrice: parseInt(validGasPrice),
gasLimit: parseInt(validGasLimit),
gasLimit: usedGasLimit,
data: new TransactionPayload(data.trim()),
chainID: chainId,
version: new TransactionVersion(1)
Expand All @@ -52,11 +66,12 @@ export function calculateFeeLimit({
networkConfig.MinGasLimit = parseInt(minGasLimit);
networkConfig.GasPerDataByte = parseInt(gasPerDataByte);
networkConfig.GasPriceModifier = parseFloat(gasPriceModifier);

try {
const bNfee = transaction.computeFee(networkConfig);
const fee = bNfee.toString(10);
return fee;
return bNfee.toString(10);
} catch (err) {
console.error(err);
return ZERO;
}
}
72 changes: 70 additions & 2 deletions src/utils/operations/tests/calculateFeeLimit.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,84 @@ describe('calculateFeeLimit tests', () => {
});
expect(feeLimit).toBe('62000000000000');
});

it('computes correct fee', () => {
const feeLimit = calculateFeeLimit({
gasLimit: '11100000',
gasPrice: '1000000000',
data: 'bid@0d59@43525a502d333663366162@25',
gasPerDataByte: '1500',
gasPerDataByte: String(GAS_PER_DATA_BYTE),
gasPriceModifier: String(GAS_PRICE_MODIFIER),
defaultGasPrice: '1000000000',
gasPriceModifier: '0.01',
chainId: 'T'
});

expect(feeLimit).toBe('210990000000000');
});

it('computes correct fee for SetGuardian tx', () => {
const feeLimit = calculateFeeLimit({
gasLimit: '',
gasPrice: (1_000_000).toString(),
data: 'SetGuardian@qwerty@12345',
chainId: 'T',
gasPerDataByte: '1',
gasPriceModifier: '1'
});

expect(feeLimit).toBe((100_000_000_000).toString()); // (minGasLimit + extra guardian gas) * gasPrice
});

it('computes correct fee for GuardAccount tx', () => {
const feeLimit = calculateFeeLimit({
gasLimit: '',
gasPrice: (1_000_000).toString(),
data: 'GuardAccount@qwerty@12345',
chainId: 'T',
gasPerDataByte: '1',
gasPriceModifier: '1'
});

expect(feeLimit).toBe((100_000_000_000).toString()); // (minGasLimit + extra guardian gas) * gasPrice
});

it('computes correct fee for UnGuardAccount tx', () => {
const feeLimit = calculateFeeLimit({
gasLimit: '',
gasPrice: (1_000_000).toString(),
data: 'UnGuardAccount@qwerty@12345',
chainId: 'T',
gasPerDataByte: '1',
gasPriceModifier: '1'
});

expect(feeLimit).toBe((100_000_000_000).toString()); // (minGasLimit + extra guardian gas) * gasPrice
});

it('computes correct fee for UnGuardAccount tx and gas limit specified', () => {
const feeLimit = calculateFeeLimit({
gasLimit: (1_000_000).toString(),
gasPrice: (1_000_000).toString(),
data: 'UnGuardAccount@qwerty@12345',
chainId: 'T',
gasPerDataByte: '1',
gasPriceModifier: '1'
});

expect(feeLimit).toBe((1_050_000_000_000).toString()); // (gasLimit + extra guardian gas) * gasPrice
});

it('computes correct fee for UnGuardAccount tx and min gas limit specified', () => {
const feeLimit = calculateFeeLimit({
gasLimit: '',
minGasLimit: (1_000_000).toString(),
gasPrice: (1_000_000).toString(),
data: 'UnGuardAccount@qwerty@12345',
chainId: 'T',
gasPerDataByte: '1',
gasPriceModifier: '1'
});

expect(feeLimit).toBe((1_050_000_000_000).toString()); // (minGasLimit + extra guardian gas) * gasPrice
});
});
1 change: 1 addition & 0 deletions src/utils/transactions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from './getInterpretedTransaction';
export * from './getTokenFromData';
export * from './getTransactionLink';
export * from './getUnHighlightedDataFieldParts';
export * from './isGuardianTx';
export * from './isTokenTransfer';
export * from './parseMultiEsdtTransferData';
export * from './parseTransactionAfterSigning';
Expand Down
21 changes: 21 additions & 0 deletions src/utils/transactions/isGuardianTx.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { GuardianActionsEnum } from 'constants/index';

export const isGuardianTx = ({
data,
onlySetGuardian
}: {
data?: string;
onlySetGuardian?: boolean;
}) => {
if (!data) {
return false;
}

if (onlySetGuardian) {
return data.startsWith(GuardianActionsEnum.SetGuardian);
}

return Object.values(GuardianActionsEnum).some((action) =>
data.startsWith(action)
);
};
8 changes: 8 additions & 0 deletions src/utils/transactions/parseTransactionAfterSigning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { PlainSignedTransaction } from '@multiversx/sdk-web-wallet-provider/out/
import { newTransaction } from 'models';
import { SignedTransactionType } from 'types';
import { TransactionServerStatusesEnum } from 'types/enums.types';
import { isGuardianTx } from './isGuardianTx';

export function parseTransactionAfterSigning(
signedTransaction: Transaction | PlainSignedTransaction
Expand All @@ -21,5 +22,12 @@ export function parseTransactionAfterSigning(
receiverUsername: transaction.getReceiverUsername().valueOf(),
status: TransactionServerStatusesEnum.pending
};

// TODO: Remove when the protocol supports usernames for guardian transactions
if (isGuardianTx({ data: parsedTransaction.data, onlySetGuardian: true })) {
delete parsedTransaction.senderUsername;
delete parsedTransaction.receiverUsername;
}

return parsedTransaction;
}
54 changes: 54 additions & 0 deletions src/utils/transactions/tests/isGuardianTx.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { GuardianActionsEnum } from 'constants/index';
import { isGuardianTx } from '../isGuardianTx';

describe('isGuardianTx Function', () => {
// Test Valid Actions
Object.values(GuardianActionsEnum).forEach((action) => {
test(`should return true for valid action starting with "${action}"`, () => {
expect(isGuardianTx({ data: `${action}ExtraInfo` })).toBe(true);
});
});

// Test when onlySetGuardian is true
test('should return true only for SetGuardian action when onlySetGuardian is true', () => {
expect(
isGuardianTx({ data: 'SetGuardianExtraInfo', onlySetGuardian: true })
).toBe(true);
expect(
isGuardianTx({
data: 'GuardAccountAdditionalData',
onlySetGuardian: true
})
).toBe(false);
expect(
isGuardianTx({
data: 'UnGuardAccountSomethingElse',
onlySetGuardian: true
})
).toBe(false);
});

// Test Invalid Actions
test('should return false for invalid actions', () => {
expect(isGuardianTx({ data: 'InvalidAction' })).toBe(false);
expect(isGuardianTx({ data: 'SetAccountGuard' })).toBe(false);
expect(isGuardianTx({ data: 'AccountUnGuard' })).toBe(false);
});

// Test Empty String
test('should return false for an empty string', () => {
expect(isGuardianTx({ data: '' })).toBe(false);
});

// Test Null or Undefined
test('should return false for null or undefined data', () => {
expect(isGuardianTx({ data: undefined })).toBe(false);
});

// Test Partial Matches
test('should return false for partial matches', () => {
expect(isGuardianTx({ data: 'Set' })).toBe(false);
expect(isGuardianTx({ data: 'Guard' })).toBe(false);
expect(isGuardianTx({ data: 'UnGuard' })).toBe(false);
});
});
Loading