Skip to content

Commit

Permalink
Merge pull request #1308 from multiversx/development
Browse files Browse the repository at this point in the history
3.0.11
  • Loading branch information
razvantomegea authored Nov 19, 2024
2 parents ac91a91 + 11099ab commit 0eca0b6
Show file tree
Hide file tree
Showing 12 changed files with 680 additions and 48 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [[v3.0.11](https://github.com/multiversx/mx-sdk-dapp/pull/1308)] - 2024-11-19

- [Updated transaction data decode functions](https://github.com/multiversx/mx-sdk-dapp/pull/1307)

## [[v3.0.10](https://github.com/multiversx/mx-sdk-dapp/pull/1305)] - 2024-11-11

- [Added ability to show transaction toast on demand](https://github.com/multiversx/mx-sdk-dapp/pull/1304)
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": "3.0.10",
"version": "3.0.11",
"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
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export const decodeForDisplay = ({
validationWarnings: []
};

if (!input.includes('@') && !input.includes('\n')) {
display.displayValue = decodeByMethod(input, decodeMethod);

return display;
}

if (input.includes('@')) {
const parts = input.split('@');
const decodedParts = getDisplayValueAndValidationWarnings({
Expand All @@ -28,20 +34,20 @@ export const decodeForDisplay = ({
decodeMethod,
display
});
display.displayValue = decodedParts.join('@');

return display;
display.displayValue = decodedParts.join('@');
}

if (input.includes('\n')) {
const parts = input.split('\n');
const initialDecodedParts = parts.map((part) => {
const base64Buffer = Buffer.from(String(part), 'base64');
const base64Buffer = Buffer.from(part, 'base64');

if (decodeMethod === DecodeMethodEnum.raw) {
return part;
} else {
return decodeByMethod(base64Buffer.toString('hex'), decodeMethod);
}

return decodeByMethod(base64Buffer.toString('hex'), decodeMethod);
});

const decodedParts =
Expand All @@ -54,11 +60,7 @@ export const decodeForDisplay = ({
: initialDecodedParts;

display.displayValue = decodedParts.join('\n');

return display;
}

display.displayValue = decodeByMethod(input, decodeMethod);

return display;
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,32 @@ import {
TransactionTokensType
} from 'types/serverTransactions.types';
import { addressIsValid } from 'utils/account/addressIsValid';
import { isUtf8, stringContainsNumbers } from 'utils/decoders';
import { isUtf8 } from 'utils/decoders';

export const decodeByMethod = (
part: string,
decodeMethod: DecodeMethodEnum | string,
transactionTokens?: TransactionTokensType
) => {
try {
switch (decodeMethod) {
case DecodeMethodEnum.text:
if (!stringContainsNumbers(part)) {
return part;
}

switch (decodeMethod) {
case DecodeMethodEnum.text:
try {
return Buffer.from(part, 'hex').toString('utf8');
case DecodeMethodEnum.decimal:
const bn = new BigNumber(part, 16);
} catch {}

return bn.toString(10);
case DecodeMethodEnum.smart:
return part;
case DecodeMethodEnum.decimal:
return part !== '' ? new BigNumber(part, 16).toString(10) : '';
case DecodeMethodEnum.smart:
try {
const bech32Encoded = Address.fromHex(part).toString();

if (addressIsValid(bech32Encoded)) {
return bech32Encoded;
}
} catch {}

try {
const decoded = Buffer.from(part, 'hex').toString('utf8');

if (!isUtf8(decoded)) {
Expand All @@ -47,20 +47,15 @@ export const decodeByMethod = (

const bn = new BigNumber(part, 16);

return bn.toString(10);
return bn.isFinite() ? bn.toString(10) : part;
} else {
return decoded;
}
} catch {}

return decoded;
case DecodeMethodEnum.raw:
default:
return part;
}
} catch (err) {
console.error(
`Error during data decoding of "${part}" as "${decodeMethod}"`,
err
);

return part;
return part;
case DecodeMethodEnum.raw:
default:
return part;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,14 @@ export const getDisplayValueAndValidationWarnings = ({
}
});

return decodeMethod === DecodeMethodEnum.smart
? getSmartDecodedParts({
parts,
decodedParts: initialDecodedParts,
identifier
})
: initialDecodedParts;
const decodedParts =
decodeMethod === DecodeMethodEnum.smart
? getSmartDecodedParts({
parts,
decodedParts: initialDecodedParts,
identifier
})
: initialDecodedParts;

return decodedParts;
};
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
const isHexValidCharacters = (str: string) => {
return str.toLowerCase().match(/[0-9a-f]/g);
export const isHexValidCharacters = (str: string) => {
return str.toLowerCase().match(/^[0-9a-f]+$/i);
};
const isHexValidLength = (str: string) => {

export const isHexValidLength = (str: string) => {
return str.length % 2 === 0;
};

export const getHexValidationWarnings = (str: string) => {
const warnings = [];

if (str && !isHexValidCharacters(str)) {
warnings.push(`Invalid Hex characters on argument @${str}`);
}

if (str && !isHexValidLength(str)) {
warnings.push(`Odd number of Hex characters on argument @${str}`);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TransactionTypesEnum } from 'types';
import { DecodeMethodEnum, TransactionTypesEnum } from 'types';
import { decodeByMethod } from './decodeByMethod';

interface SmartDecodedPartsType {
Expand All @@ -15,12 +15,15 @@ export const getSmartDecodedParts = ({
const updatedParts = [...decodedParts];

if (parts[0] === TransactionTypesEnum.ESDTNFTTransfer && parts[2]) {
updatedParts[2] = decodeByMethod(parts[2], 'decimal');
updatedParts[2] = decodeByMethod(parts[2], DecodeMethodEnum.decimal);
}

if (identifier === TransactionTypesEnum.ESDTNFTTransfer && parts[1]) {
const base64Buffer = Buffer.from(String(parts[1]), 'base64');
updatedParts[1] = decodeByMethod(base64Buffer.toString('hex'), 'decimal');
updatedParts[1] = decodeByMethod(
base64Buffer.toString('hex'),
DecodeMethodEnum.decimal
);
}

return updatedParts;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { Address } from '@multiversx/sdk-core/out';
import { DecodeMethodEnum } from 'types';
import { addressIsValid } from 'utils/account/addressIsValid';
import { isUtf8 } from 'utils/decoders';
import { decodeByMethod } from '../helpers';

jest.mock('@multiversx/sdk-core/out', () => ({
Address: {
fromHex: jest.fn()
}
}));

jest.mock('utils/account/addressIsValid');
jest.mock('utils/decoders');

describe('decodeByMethod', () => {
beforeEach(() => {
jest.clearAllMocks();
});

describe('text decode method', () => {
it('should decode hex to utf8 text', () => {
const hexString = Buffer.from('Hello').toString('hex');
const result = decodeByMethod(hexString, DecodeMethodEnum.text);
expect(result).toBe('Hello');
});

it('should return empty string if the hex is invalid', () => {
const invalidHex = '{test: test}';
const result = decodeByMethod(invalidHex, DecodeMethodEnum.text);
expect(result).toBe('');
});
});

describe('decimal decode method', () => {
it('should convert hex to decimal', () => {
const result = decodeByMethod('a', DecodeMethodEnum.decimal);
expect(result).toBe('10');
});

it('should return empty string for empty input', () => {
const result = decodeByMethod('', DecodeMethodEnum.decimal);
expect(result).toBe('');
});
});

describe('smart decode method', () => {
it('should return bech32 address when valid', () => {
const mockAddress =
'erd1zwq3qaa3vk5suenlkj4cf0ullwefa6h3n2394k25pxv4sz0pwhhsj9u9vk';

(Address.fromHex as jest.Mock).mockReturnValue({
toString: () => mockAddress
});

(addressIsValid as jest.Mock).mockReturnValue(true);

const result = decodeByMethod('validHex', DecodeMethodEnum.smart);
expect(result).toBe(mockAddress);
});

it('should decode to utf8 when possible and valid', () => {
(Address.fromHex as jest.Mock).mockImplementation(() => {
throw new Error();
});
(isUtf8 as jest.Mock).mockReturnValue(true);

const hexString = Buffer.from('ValidText').toString('hex');
const result = decodeByMethod(hexString, DecodeMethodEnum.smart);
expect(result).toBe('ValidText');
});

it('should check for tokens when utf8 decoded but invalid', () => {
const mockTokens = {
esdts: ['token1'],
nfts: ['nft1']
};

(Address.fromHex as jest.Mock).mockImplementation(() => {
throw new Error();
});

(isUtf8 as jest.Mock).mockReturnValue(false);

const hexString = Buffer.from('token1').toString('hex');
const result = decodeByMethod(
hexString,
DecodeMethodEnum.smart,
mockTokens
);
expect(result).toBe('token1');
});

it('should convert to decimal when no other conditions met', () => {
(Address.fromHex as jest.Mock).mockImplementation(() => {
throw new Error();
});
(isUtf8 as jest.Mock).mockReturnValue(false);

const result = decodeByMethod('a', DecodeMethodEnum.smart);
expect(result).toBe('10');
});

it('should return original part when all decoding fails', () => {
(Address.fromHex as jest.Mock).mockImplementation(() => {
throw new Error();
});

const invalidInput = 'invalid';
const result = decodeByMethod(invalidInput, DecodeMethodEnum.smart);
expect(result).toBe(invalidInput);
});
});

describe('raw decode method', () => {
it('should return original part', () => {
const part = 'rawData';
const result = decodeByMethod(part, DecodeMethodEnum.raw);
expect(result).toBe(part);
});
});
});
Loading

0 comments on commit 0eca0b6

Please sign in to comment.