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

3.0.11 #1308

Merged
merged 3 commits into from
Nov 19, 2024
Merged

3.0.11 #1308

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
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
Loading