Skip to content

Commit

Permalink
feat: new Cairo types
Browse files Browse the repository at this point in the history
  • Loading branch information
PhilippeR26 committed Aug 10, 2023
1 parent 9b77cf6 commit f9db09e
Show file tree
Hide file tree
Showing 9 changed files with 16,693 additions and 11,658 deletions.
15,236 changes: 9,214 additions & 6,022 deletions __mocks__/cairo/helloCairo2/compiled.casm

Large diffs are not rendered by default.

12,983 changes: 7,354 additions & 5,629 deletions __mocks__/cairo/helloCairo2/compiled.json

Large diffs are not rendered by default.

55 changes: 54 additions & 1 deletion __mocks__/cairo/helloCairo2/hello.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// coded with Cairo v2.0.0
use array::ArrayTrait;
use array::SpanTrait;
use option::OptionTrait;
Expand All @@ -15,6 +16,9 @@ use starknet::storage_read_syscall;
use starknet::storage_write_syscall;
use starknet::storage_address_from_base_and_offset;
use starknet::storage_base_address_from_felt252;
use starknet::ClassHash;
use starknet::EthAddress;
use starknet::Span;

use traits::Into;
use starknet::storage_access::StorageAddressSerde;
Expand Down Expand Up @@ -141,6 +145,15 @@ trait IHelloStarknet<TContractState> {
fn option_order_input(self: @TContractState, inp: Option<Order>) -> u16;
fn enum_result_output(self: @TContractState, val1: u16) -> Result<Order, u16>;
fn enum_result_input(self: @TContractState, inp: Result<Order, u16>) -> u16;
fn new_types(
self: @TContractState, ch: ClassHash, eth_addr: EthAddress, contr_address: ContractAddress
) -> (ClassHash, EthAddress, ContractAddress);
fn new_span(self: @TContractState, my_span: Span<u16>) -> Span<u16>;
fn array_new_types(
self: @TContractState,
tup: (ContractAddress, EthAddress, ClassHash),
tupa: (Array<ContractAddress>, Array<EthAddress>, Array<ClassHash>)
) -> (Array<ContractAddress>, Array<EthAddress>, Array<ClassHash>);
}

// MAIN APP
Expand All @@ -165,7 +178,9 @@ mod HelloStarknet {
use starknet::storage_write_syscall;
use starknet::storage_address_from_base_and_offset;
use starknet::storage_base_address_from_felt252;

use starknet::ClassHash;
use starknet::EthAddress;
use starknet::Span;
use traits::Into;
use starknet::storage_access::StorageAddressSerde;
use box::BoxTrait;
Expand Down Expand Up @@ -447,6 +462,22 @@ mod HelloStarknet {
MyEnum::Response(Order { p1: 1, p2: val1 })
}

// MyEnum as input
fn my_enum_input(self: @ContractState, customEnum: MyEnum) -> u16 {
match customEnum {
MyEnum::Response(my_order) => {
return my_order.p2;
},
MyEnum::Warning(val) => {
return 0x13_u16;
},
MyEnum::Error(a) => {
return a;
}
}
}


// MyEnum as input
fn my_enum_input(self: @ContractState, customEnum: MyEnum) -> u16 {
match customEnum {
Expand Down Expand Up @@ -505,6 +536,28 @@ mod HelloStarknet {
return y;
}
}

// new types from Cairo
fn new_types(
self: @ContractState,
ch: ClassHash,
eth_addr: EthAddress,
contr_address: ContractAddress
) -> (ClassHash, EthAddress, ContractAddress) {
(ch, eth_addr, contr_address)
}

fn array_new_types(
self: @ContractState,
tup: (ContractAddress, EthAddress, ClassHash),
tupa: (Array<ContractAddress>, Array<EthAddress>, Array<ClassHash>)
) -> (Array<ContractAddress>, Array<EthAddress>, Array<ClassHash>) {
let (aca, aetha, ach) = tupa;
(aca, aetha, ach)
}

fn new_span(self: @ContractState, my_span: Span<u16>) -> Span<u16> {
my_span
}
}
}
35 changes: 32 additions & 3 deletions __tests__/cairo1v2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ describe('Cairo 1 Devnet', () => {
expect(balance).toBe(200n);
});

test('Cairo 1 Contract Interaction - uint 8, 16, 32, 64, 128', async () => {
test('Cairo 1 Contract Interaction - uint 8, 16, 32, 64, 128, litterals', async () => {
const tx = await cairo1Contract.increase_balance_u8(255n);
await account.waitForTransaction(tx.transaction_hash);
const balance = await cairo1Contract.get_balance_u8();
Expand Down Expand Up @@ -189,12 +189,36 @@ describe('Cairo 1 Devnet', () => {
expect(status).toBe(true);
});

test('Cairo 1 Contract Interaction - ContractAddress', async () => {
test('Cairo 1 Contract Interaction - ContractAddress, ClassHash, EthAddress', async () => {
const tx = await cairo1Contract.set_ca('123');
await account.waitForTransaction(tx.transaction_hash);
const status = await cairo1Contract.get_ca();

expect(status).toBe(123n);

// new types Cairo v2.0.0
const compiled = cairo1Contract.populate('new_types', {
ch: 123456789n,
eth_addr: 987654321n,
contr_address: 657563474357n,
});
const result = await cairo1Contract.call('new_types', compiled.calldata as Calldata);
expect(result).toStrictEqual({ '0': 123456789n, '1': 987654321n, '2': 657563474357n });

const myCalldata = new CallData(compiledC1v2.abi); // test arrays
const compiled2 = myCalldata.compile('array_new_types', {
tup: cairo.tuple(256, '0x1234567890', '0xe3456'),
tupa: cairo.tuple(
['0x1234567890', '0xe3456'], // ContractAddress
['0x1234567891', '0xe3457'], // EthAddress
['0x1234567892', '0xe3458'] // ClassHash
),
});
const res1 = await cairo1Contract.call('array_new_types', compiled2);
expect(res1).toStrictEqual({
'0': [78187493520n, 930902n],
'1': [78187493521n, 930903n],
'2': [78187493522n, 930904n],
});
});

test('Cairo1 simple getStorageAt variables retrieval', async () => {
Expand Down Expand Up @@ -262,6 +286,11 @@ describe('Cairo 1 Devnet', () => {

const status2 = await cairo1Contract.echo_array_bool([true, true, false, false]);
expect(status2).toEqual([true, true, false, false]);

// Span type
const comp = cairo1Contract.populate('new_span', { my_span: [1, 2, 3] });
const resp = await cairo1Contract.call('new_span', comp.calldata as Calldata);
expect(resp).toEqual([1n, 2n, 3n]);
});

test('Cairo 1 Contract Interaction - echo flat un-nested Struct', async () => {
Expand Down
5 changes: 5 additions & 0 deletions src/types/calldata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ export enum Uint {
u128 = 'core::integer::u128',
u256 = 'core::integer::u256', // This one is struct
}

export enum Litteral {
ClassHash = 'core::starknet::class_hash::ClassHash',
ContractAddress = 'core::starknet::contract_address::ContractAddress',
}
7 changes: 5 additions & 2 deletions src/utils/calldata/cairo.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { Abi, AbiEnums, AbiStructs, BigNumberish, Uint, Uint256 } from '../../types';
import { Abi, AbiEnums, AbiStructs, BigNumberish, Litteral, Uint, Uint256 } from '../../types';
import { isBigInt, isHex, isStringWholeNumber } from '../num';
import { encodeShortString, isShortString, isText } from '../shortString';
import { UINT_128_MAX, isUint256 } from '../uint256';

export const isLen = (name: string) => /_len$/.test(name);
export const isTypeFelt = (type: string) => type === 'felt' || type === 'core::felt252';
export const isTypeArray = (type: string) =>
/\*/.test(type) || type.startsWith('core::array::Array::');
/\*/.test(type) ||
type.startsWith('core::array::Array::') ||
type.startsWith('core::array::Span::');
export const isTypeTuple = (type: string) => /^\(.*\)$/i.test(type);
export const isTypeNamedTuple = (type: string) => /\(.*\)/i.test(type) && type.includes(':');
export const isTypeStruct = (type: string, structs: AbiStructs) => type in structs;
export const isTypeEnum = (type: string, enums: AbiEnums) => type in enums;
export const isTypeOption = (type: string) => type.startsWith('core::option::Option::');
export const isTypeResult = (type: string) => type.startsWith('core::result::Result::');
export const isTypeUint = (type: string) => Object.values(Uint).includes(type as Uint);
export const isTypeLitteral = (type: string) => Object.values(Litteral).includes(type as Litteral);
export const isTypeUint256 = (type: string) => type === 'core::integer::u256';
export const isTypeBool = (type: string) => type === 'core::bool';
export const isTypeContractAddress = (type: string) =>
Expand Down
5 changes: 5 additions & 0 deletions src/utils/calldata/requestParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ function parseCalldataValue(
return parseUint256(element);
}

if (type === 'core::starknet::eth_address::EthAddress')
return parseBaseTypes(type, element as BigNumberish);

const { members } = structs[type];
const subElement = element as any;

Expand Down Expand Up @@ -265,6 +268,8 @@ export function parseCalldataField(
}
return parseCalldataValue(value, input.type, structs, enums);

case type === 'core::starknet::eth_address::EthAddress':
return parseBaseTypes(type, value);
// Struct or Tuple
case isTypeStruct(type, structs) || isTypeTuple(type) || isTypeUint256(type):
return parseCalldataValue(value as ParsedStruct | BigNumberish[], type, structs, enums);
Expand Down
18 changes: 18 additions & 0 deletions src/utils/calldata/responseParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ function parseBaseTypes(type: string, it: Iterator<string>) {
const low = it.next().value;
const high = it.next().value;
return uint256ToBN({ low, high });
case type === 'core::starknet::eth_address::EthAddress':
temp = it.next().value;
return BigInt(temp);
default:
temp = it.next().value;
return BigInt(temp);
Expand Down Expand Up @@ -76,8 +79,23 @@ function parseResponseValue(
return uint256ToBN({ low, high });
}

// type c1 array
if (isTypeArray(element.type)) {
// eslint-disable-next-line no-case-declarations
const parsedDataArr: (BigNumberish | ParsedStruct | boolean | any[])[] = [];
const el = { name: '', type: getArrayType(element.type) };
const len = BigInt(responseIterator.next().value); // get length
while (parsedDataArr.length < len) {
parsedDataArr.push(parseResponseValue(responseIterator, el, structs));
}
return parsedDataArr;
}

// type struct
if (element.type in structs && structs[element.type]) {
if (element.type === 'core::starknet::eth_address::EthAddress') {
return parseBaseTypes(element.type, responseIterator);
}
return structs[element.type].members.reduce((acc, el) => {
acc[el.name] = parseResponseValue(responseIterator, el, structs, enums);
return acc;
Expand Down
7 changes: 6 additions & 1 deletion www/docs/guides/define_call_message.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const decimals: BigNumberish = 18;

If your Cairo smart contract is waiting for a:

### felt, u8, u16, u32, usize, u64, u128, felt252, address
### felt, u8, u16, u32, usize, u64, u128, felt252, ContractAddress, EthAddress, ClassHash

Starknet is waiting for a felt.
You can send to Starknet.js methods: bigNumberish.
Expand All @@ -58,6 +58,9 @@ You can send to Starknet.js methods: bigNumberish.
await myContract.my_function(12,"13","0xe",15n);
```

> `EthAddress` is limited to 160 bits.
> `felt, felt252, ClassHash` and `ContractAddress` are limited to 252 bits.
### bool

Starknet is waiting for a felt, containing 0 or 1.
Expand Down Expand Up @@ -193,6 +196,8 @@ await myContract.my_function(myArray);

> Do not add the `array_len` parameter before your array. Starknet.js will manage this element automatically.
> It's also applicable for Cairo `Span` type.
### complex types

You can mix and nest literals, arrays, structs, and tuples.
Expand Down

0 comments on commit f9db09e

Please sign in to comment.