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

chore: calldata utils improvements & tests #1221

Merged
merged 31 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1abe438
chore: add tests for calldata enums
lukasaric Sep 6, 2024
cd0ac67
chore: improve JS docs in formatter and tuple
lukasaric Sep 6, 2024
27a1444
chore: add tuple tests
lukasaric Sep 6, 2024
53d3387
chore: add basic tests for validate
lukasaric Sep 6, 2024
904e5b6
chore: cover bytes31 validation with tests
lukasaric Sep 6, 2024
79bbae6
chore: cover more test cases for uint validation
lukasaric Sep 6, 2024
1619892
chore: cover all uint cases
lukasaric Sep 6, 2024
31bbefb
chore: cover array type validation
lukasaric Sep 6, 2024
d20e58f
Merge branch 'develop' into chore/calldata-utils
lukasaric Sep 6, 2024
beac21e
chore: add tuple validation & and more array per type cases
lukasaric Sep 6, 2024
87994b8
chore: add struct validation tests & improve Literal const
lukasaric Sep 7, 2024
54c1cec
chore: cover enums validation tests in detail
lukasaric Sep 7, 2024
9e89ace
chore: decouple eth address constant from literal
lukasaric Sep 7, 2024
f95adc0
chore: add non zero type validation
lukasaric Sep 8, 2024
e03b116
chore: add missing enum & struct array cases
lukasaric Sep 8, 2024
cd9815a
chore: add byte array util tests
lukasaric Sep 8, 2024
b07c6e8
chore: add cairo util tests partially
lukasaric Sep 8, 2024
84a4b32
chore: improve cairo test coverage
lukasaric Sep 8, 2024
ef39d15
chore: summarize cairo tests
lukasaric Sep 8, 2024
5e29a19
chore: add formatter tests & fix JS docs in it
lukasaric Sep 9, 2024
8615c2a
chore: improve obj keys naming
lukasaric Sep 9, 2024
5a48641
chore: change obj keys nomenclature in js docs
lukasaric Sep 9, 2024
4d1cf4e
chore: add tests for 2.0.0 parser
lukasaric Sep 9, 2024
9d86594
chore: add parser 1 tests
lukasaric Sep 9, 2024
9fff468
chore: add parser index tests
lukasaric Sep 9, 2024
c01a911
chore: extract test abi factories
lukasaric Sep 9, 2024
319bb05
chore: formatting fixes
lukasaric Sep 10, 2024
ca3edbb
chore: add JS docs for abi parser utils
lukasaric Sep 10, 2024
071c5c6
chore: fix typo
lukasaric Sep 10, 2024
e13c6a6
chore: fix typo that caused failing test
lukasaric Sep 10, 2024
80161a8
chore: move factories to __tests__
lukasaric Sep 11, 2024
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
47 changes: 47 additions & 0 deletions __mocks__/factories/abi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { InterfaceAbi, AbiEntry, AbiEnums, AbiStructs, FunctionAbi } from '../../src';

export const getAbiEntry = (type: string): AbiEntry => ({ name: 'test', type });

export const getFunctionAbi = (inputsType: string): FunctionAbi => ({
inputs: [getAbiEntry(inputsType)],
name: 'test',
outputs: [getAbiEntry(inputsType)],
stateMutability: 'view',
type: 'function',
});

export const getInterfaceAbi = (functionAbiType: string = 'struct'): InterfaceAbi => ({
items: [getFunctionAbi(functionAbiType)],
name: 'test_interface_abi',
type: 'interface',
});

export const getAbiStructs = (): AbiStructs => ({
struct: {
members: [
{
name: 'test_name',
type: 'test_type',
offset: 1,
},
],
size: 2,
name: 'cairo__struct',
type: 'struct',
},
});

export const getAbiEnums = (): AbiEnums => ({
enum: {
variants: [
{
name: 'test_name',
type: 'cairo_struct_variant',
offset: 1,
},
],
size: 2,
name: 'test_cairo',
type: 'enum',
},
});
lukasaric marked this conversation as resolved.
Show resolved Hide resolved
23 changes: 23 additions & 0 deletions __tests__/utils/calldata/byteArray.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { stringFromByteArray, byteArrayFromString } from '../../../src/utils/calldata/byteArray';

describe('stringFromByteArray', () => {
test('should return string from Cairo byte array', () => {
const str = stringFromByteArray({
data: [],
pending_word: '0x414243444546474849',
pending_word_len: 9,
});
expect(str).toEqual('ABCDEFGHI');
});
});

describe('byteArrayFromString', () => {
test('should return Cairo byte array from string', () => {
const byteArray = byteArrayFromString('ABCDEFGHI');
expect(byteArray).toEqual({
data: [],
pending_word: '0x414243444546474849',
pending_word_len: 9,
});
});
});
323 changes: 323 additions & 0 deletions __tests__/utils/calldata/cairo.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,323 @@
import {
isLen,
isTypeFelt,
isTypeUint,
isTypeUint256,
isTypeArray,
uint256,
uint512,
isTypeTuple,
isTypeNamedTuple,
isTypeStruct,
isTypeEnum,
isTypeOption,
isTypeResult,
isTypeLiteral,
isTypeBool,
isTypeContractAddress,
isTypeEthAddress,
isTypeBytes31,
isTypeByteArray,
isTypeSecp256k1Point,
isCairo1Type,
getArrayType,
isCairo1Abi,
isTypeNonZero,
getAbiContractVersion,
tuple,
felt,
} from '../../../src/utils/calldata/cairo';
import { ETH_ADDRESS, Literal, Uint, type ContractVersion, NON_ZERO_PREFIX } from '../../../src';
import {
getFunctionAbi,
getAbiEnums,
getAbiStructs,
getInterfaceAbi,
} from '../../../__mocks__/factories/abi';

describe('isLen', () => {
test('should return true if name ends with "_len"', () => {
expect(isLen('test_len')).toEqual(true);
});

test('should return false if name does not end with "_len"', () => {
expect(isLen('test')).toEqual(false);
});
});

describe('isTypeFelt', () => {
test('should return true if given type is Felt', () => {
expect(isTypeFelt('felt')).toEqual(true);
expect(isTypeFelt('core::felt252')).toEqual(true);
});

test('should return false if given type is not Felt', () => {
expect(isTypeFelt('core::bool')).toEqual(false);
});
});

describe('isTypeArray', () => {
test('should return true if given type is an Array', () => {
expect(isTypeArray('core::array::Array::<core::bool>')).toEqual(true);
expect(isTypeArray('core::array::Span::<core::bool>')).toEqual(true);
expect(isTypeArray('felt*')).toEqual(true);
});

test('should return false if given type is not an Array ', () => {
expect(isTypeArray('core::bool')).toEqual(false);
});
});

describe('isTypeTuple', () => {
test('should return true if given type is Tuple', () => {
expect(isTypeTuple('(core::bool, felt)')).toEqual(true);
});

test('should return false if given type is not Tuple ', () => {
expect(isTypeTuple('core::bool')).toEqual(false);
});
});

describe('isTypeNamedTuple', () => {
test('should return true if given type is named Tuple', () => {
expect(isTypeNamedTuple('(core::bool, core::bool)')).toEqual(true);
expect(isTypeNamedTuple('(core::bool, felt)')).toEqual(true);
});

test('should return false if given type is not named Tuple ', () => {
expect(isTypeNamedTuple('(felt, felt)')).toEqual(false);
});
});

describe('isTypeStruct', () => {
test('should return true if given type is Struct', () => {
expect(isTypeStruct('struct', getAbiStructs())).toEqual(true);
});

test('should return false if given type is not Struct', () => {
expect(isTypeStruct('struct', { test: getAbiStructs().struct })).toEqual(false);
});
});

describe('isTypeEnum', () => {
test('should return true if given type is Enum', () => {
expect(isTypeEnum('enum', getAbiEnums())).toEqual(true);
});

test('should return false if given type is not Enum', () => {
expect(isTypeEnum('enum', { test: getAbiEnums().enum })).toEqual(false);
});
});

describe('isTypeOption', () => {
test('should return true if given type is Option', () => {
expect(isTypeOption('core::option::Option::core::bool')).toEqual(true);
});

test('should return false if given type is not Option', () => {
expect(isTypeOption('core::bool')).toEqual(false);
});
});

describe('isTypeResult', () => {
test('should return true if given type is Result', () => {
expect(isTypeResult('core::result::Result::core::bool')).toEqual(true);
});

test('should return false if given type is not Result', () => {
expect(isTypeResult('core::bool')).toEqual(false);
});
});

describe('isTypeUint', () => {
test('should return true if given type is Uint', () => {
Object.values(Uint).forEach((uint) => {
expect(isTypeUint(uint)).toEqual(true);
});
});

test('should return false if given type is not Uint', () => {
expect(isTypeUint('core::bool')).toEqual(false);
});
});

describe('isTypeUint256', () => {
test('should return true if given type is Uint256', () => {
expect(isTypeUint256('core::integer::u256')).toEqual(true);
});

test('should return false if given type is not Uint256', () => {
expect(isTypeUint256('core::bool')).toEqual(false);
});
});

describe('isTypeLiteral', () => {
test('should return true if given type is Literal', () => {
Object.values(Literal).forEach((literal) => {
expect(isTypeLiteral(literal)).toEqual(true);
});
});

test('should return false if given type is not Literal', () => {
expect(isTypeLiteral('core::bool')).toEqual(false);
});
});

describe('isTypeBool', () => {
test('should return true if given type is Bool', () => {
expect(isTypeBool('core::bool')).toEqual(true);
});

test('should return false if given type is not Bool', () => {
expect(isTypeBool(Uint.u8)).toEqual(false);
});
});

describe('isTypeContractAddress', () => {
test('should return true if given type is ContractAddress', () => {
expect(isTypeContractAddress(Literal.ContractAddress)).toEqual(true);
});

test('should return false if given type is not ContractAddress', () => {
expect(isTypeContractAddress(Uint.u8)).toEqual(false);
});
});

describe('isTypeEthAddress', () => {
test('should return true if given type is EthAddress', () => {
expect(isTypeEthAddress(ETH_ADDRESS)).toEqual(true);
});

test('should return false if given type is not EthAddress', () => {
expect(isTypeEthAddress(Literal.ContractAddress)).toEqual(false);
});
});

describe('isTypeBytes31', () => {
test('should return true if given type is Bytes31', () => {
expect(isTypeBytes31('core::bytes_31::bytes31')).toEqual(true);
});

test('should return false if given type is not Bytes31', () => {
expect(isTypeBytes31('core::bool')).toEqual(false);
});
});

describe('isTypeByteArray', () => {
test('should return true if given type is ByteArray', () => {
expect(isTypeByteArray('core::byte_array::ByteArray')).toEqual(true);
});

test('should return false if given type is not ByteArray', () => {
expect(isTypeByteArray('core::bool')).toEqual(false);
});
});

describe('isTypeSecp256k1Point', () => {
test('should return true if given type is Secp256k1Point', () => {
expect(isTypeSecp256k1Point(Literal.Secp256k1Point)).toEqual(true);
});

test('should return false if given type is not Secp256k1Point', () => {
expect(isTypeSecp256k1Point('core::bool')).toEqual(false);
});
});

describe('isCairo1Type', () => {
test('should return true if given type is Cairo1', () => {
expect(isCairo1Type('core::bool')).toEqual(true);
});

test('should return false if given type is not Cairo1', () => {
expect(isCairo1Type('felt')).toEqual(false);
});
});

describe('getArrayType', () => {
test('should extract type from an array', () => {
expect(getArrayType('felt*')).toEqual('felt');
expect(getArrayType('core::array::Array::<core::bool>')).toEqual('core::bool');
});
});

describe('isTypeNonZero', () => {
test('should return true if given type is NonZero', () => {
expect(isTypeNonZero(`${NON_ZERO_PREFIX}core::bool`)).toEqual(true);
});

test('should return false if given type is not NonZero', () => {
expect(isTypeNonZero('core::bool')).toEqual(false);
});
});

describe('isCairo1Abi', () => {
test('should return true if ABI comes from Cairo 1 contract', () => {
expect(isCairo1Abi([getInterfaceAbi()])).toEqual(true);
});

test('should return false if ABI comes from Cairo 0 contract', () => {
expect(isCairo1Abi([getFunctionAbi('felt')])).toEqual(false);
});

test('should throw an error if ABI does not come from Cairo 1 contract ', () => {
expect(() => isCairo1Abi([{}])).toThrow(new Error('Unable to determine Cairo version'));
});
});

describe('getAbiContractVersion', () => {
test('should return Cairo 0 contract version', () => {
const contractVersion: ContractVersion = getAbiContractVersion([getFunctionAbi('felt')]);
expect(contractVersion).toEqual({ cairo: '0', compiler: '0' });
});

test('should return Cairo 1 with compiler 2 contract version', () => {
const contractVersion: ContractVersion = getAbiContractVersion([getInterfaceAbi()]);
expect(contractVersion).toEqual({ cairo: '1', compiler: '2' });
});

test('should return Cairo 1 with compiler 1 contract version', () => {
const contractVersion: ContractVersion = getAbiContractVersion([getFunctionAbi('core::bool')]);
expect(contractVersion).toEqual({ cairo: '1', compiler: '1' });
});

test('should return undefined values for cairo and compiler', () => {
const contractVersion: ContractVersion = getAbiContractVersion([{}]);
expect(contractVersion).toEqual({ cairo: undefined, compiler: undefined });
});
});

describe('uint256', () => {
test('should create Uint256 Cairo type', () => {
const uint = uint256('892349863487563453485768723498');
expect(uint).toEqual({ low: '892349863487563453485768723498', high: '0' });
});
});

describe('uint512', () => {
test('should create Uint512 Cairo type', () => {
const uint = uint512('345745685892349863487563453485768723498');
expect(uint).toEqual({
limb0: '5463318971411400024188846054000512042',
limb1: '1',
limb2: '0',
limb3: '0',
});
});
});

describe('tuple', () => {
test('should create unnamed Cairo type tuples', () => {
const tuples = [tuple(true, false), tuple(1, '0x101', 16)];
expect(tuples).toEqual([
{ '0': true, '1': false },
{ '0': 1, '1': '0x101', '2': 16 },
]);
});
});

describe('felt', () => {
test('should create Cairo type felts', () => {
const felts = [felt('test'), felt(256n), felt(1234)];
expect(felts).toEqual(['1952805748', '256', '1234']);
});
});
Loading