Transcoding Inputs for Starknet Contracts
Starknet contracts, also known as zk-STARK contracts, are a type of smart contract that utilizes zero-knowledge proof technology to provide privacy and security to its users and these contracts are written in cairo language. Unlike traditional smart contracts written in Solidity, the inputs to a Starknet contract are structurally different, making it necessary to understand how to interact with them effectively.
Warp is a transpiler tool developed by Nethermind, which is capable of converting Solidity smart contracts into Starknet contracts. However, the input parameters (calldata)
for the Solidity smart contracts may differ slightly from those required for Starknet contracts written in Cairo. This is due to the differences in the programming languages used and the specific requirements of the respective platforms.
This is a guide on the structural differences between inputs i.e calldata in a Starknet contract (Warped Version) compared to a Solidity smart contract:
Exampleβ
Consider a solidity contract (contract.sol
):
contract A {
function increment(uint x) public pure returns (uint) {
return x + 1;
}
}
The transpiled i.e Warped
version of the above contract (call it contract.cairo
) would be as follows:
%lang starknet
from starkware.cairo.common.uint256 import Uint256
...
@view
func increment_7cf5dab0{syscall_ptr: felt*, range_check_ptr: felt}(__warp_0_x: Uint256) -> (
__warp_1: Uint256
) {
alloc_locals;
warp_external_input_check_int256(__warp_0_x);
let (__warp_se_0) = warp_add256(__warp_0_x, Uint256(low=1, high=0));
return (__warp_se_0,);
}
...
The function increment_7cf5dab0
in the cairo contract corresponds to increment
function in solidity contract.
When interacting with a Solidity contract, you would typically pass an integer value, such as "45", as a single parameter "x" to the function "increment". However, when working with the equivalent function "increment_7cf5dab0" in a Cairo-based Starknet contract, you would need to pass two values "45, 0" to accurately represent the same integer value. This is because the "Uint256" data type in Starknet contracts is implemented as a pair of felt
s, which are a type of primitive field element in Cairo. This difference in representation highlights the distinct differences between the two platforms and the impact it has on the way input parameters are passed to functions.
Note : Warp supports both Solidity and Cairo contract input formats through its CLI. The default is Solidity format, but Cairo format can be used by passing the "--use_cairo_abi" flag
# for solidity type input
warp invoke --function increment --inputs 45 --address `contract_address`
# for cairo type input
warp invoke --function increment_7cf5dab0 --inputs 45 0 --address `contract_address` --use_cairo_abi
Note: Warp internally calls encodeInputs and decodeOutputs to make solidity inputs compatible for Warped contracts and decode it back to solidity type output from cairo type outputs respectively.
Comparision of inputsβ
This section provides a detailed comparison of input data types used in smart contracts, including examples to demonstrate the differences.
The goal is to give a comprehensive understanding of the variations in input parameters required for contract functions.
It shows how solidity types in solidity contracts transforms to a cairo type in Starknet contracts after warping.
Value typesβ
The following types are also called value types because variables of these types will always be passed by value, i.e. they are always copied when they are used as function arguments or in assignments.
Booleansβ
bool
: The possible values are constants true
and false
, these are represented in a single felt
in cairo.
0 for false
and 1 for true
.
Integersβ
Signed and unsigned integers of various sizes (from 8 bits to 256 bits). All integers are represented in a single felt
except for 256 width integers. 256 bits integers are represented in a pair of felt
s . Negative Integers are represented as 2-complement of negative of the number.
Example: -1(int256)
is represented as Uint256(0xffffffffffffffffffffffffffffffff, 0xffffffffffffffffffffffffffffffff)
in cairo. It should be passed as 0xffffffffffffffffffffffffffffffff, 0xffffffffffffffffffffffffffffffff
.
Addresses/Contract Types/Address Literalsβ
Addresses are represented in a single felt
.
Fixed-size byte arraysβ
The value types bytes1, bytes2, bytes3, β¦, bytes32 hold a sequence of bytes from one to up to 32. Variables with width upto 248 bits are represented in a single felt
. bytes32
is represented in a pair of felt
s (Uint256).
Enumsβ
Enum variables are converted into a single felt in accord with the position of enum definition.
User Defined Value Typesβ
A user defined value type allows creating a zero cost abstraction over an elementary value type.
Representation is the same as the elementary value type over at which it has been abstracted.
Reference typesβ
String/String Literalsβ
Strings are represented as a felt
array with the leading element representing the number of elements in the array, typically represented as (arr_len: felt, arr:felt*)
in the cairo lang.
Example: 'warp'
is represented as 4 119 97 114 112
.
bytesβ
Same as string
, use byte values in-place instead of ascii values of characters.
Arraysβ
Arrays can have a compile-time fixed size, or they can have a dynamic size.
Fixed sized arrays are represented as tuple of base types with length equals length of the array.
Example:
uint[3][2]
is represented as(Uint256, Uint256, Uint256), (Uint256, Uint256, Uint256))
. So, If you want to pass[[12, 13, 14], [13, 14, 15]]
then you have to provide12, 0, 13, 0, 14, 0, 13, 0, 14, 0, 15, 0
to interact with warped contracts.Dynamic sized arrays are represented as
(arr_len: felt, arr: (base type in cairo)*)
, the first value represents number of elements in the array.Example:
uint[3][]
is represented asarr_len : felt, arr : (Uint256, Uint256, Uint256)*
. So, If you want to pass[[12, 13, 14], [13, 14, 15]]
then you've to provide2, 12, 0, 13, 0, 14, 0, 13, 0, 14, 0, 15, 0
to interact with warped contracts.
Structsβ
Solidity provides a way to define new types in the form of structs.
Structs are represented as structs
in cairo after warping but with members are of corresponding cairo type.
Example:
struct S {
uint x;
uint y;
}
corresponds to
struct S_699172d7{
x : Uint256,
y : Uint256,
}
Note S_699172d7
is the struct signature of the S
type in Solidity.
So, If you want to pass an input of type S
with values S(23, 45)
, then you have to provide 23 0 45 0
in the inputs to interact with the warped contracts.
Table for referenceβ
The table provides an extensive list of Solidity data types with corresponding example inputs for use with Cairo contracts (Warped contracts), which are the output of transpiling Solidity contracts. The table demonstrates the type of inputs transformation required for each Solidity type to interact effectively with the Cairo contracts.
Solidity Type | Cairo Type | Example solc input args | Example cairo input args |
---|---|---|---|
bool | felt | true | 1 |
intX (X β [8, 16,..., 248]) | felt | 45 | 45 |
int/int256 | Uint256(felt : low 128 bits , felt : high 128 bits ) | 45 | 45, 0 |
uintX (X β [8, 16,..., 248]) | felt | 45 | 45 |
uint/uint256 | Uint256(felt : low 128 bits , felt : high 128 bits ) | 45 | 45, 0 |
address (160 bits) | felt (251 bits) | 0x0000000000000000000000000000000000000001 | 0x0000000000000000000000000000000000000001 |
contract | felt(251 bit contract address ) | 0x0000000000000000000000000000000000000001 | 0x0000000000000000000000000000000000000001 |
bytesX (X β [1, 2,..., 31]) | felt | 0x3232 (bytes2 ) | 0x3232 (felt) |
bytes32 | Uint256(felt : low 128 bits , felt : high 128 bits ) | 45 | 45, 0 |
Integer Literals | felt | -1 | 0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff (two's complement) |
Rational Literals | N/A | N/A | N/A |
string/bytes | arr_len : felt, arr: felt* | "abc" | 3 (length), 97, 98, 99 |
Dynamic Arrays (address[] ) | arrlen: felt, arr : (base Type) (felt_) | [0x0, 0x1, 0x2] | 3 0 1 2 |
Fixed Arrays (uint[3] ) | tuples of base Type ((Uint256, Uint256, Uint256) ) | [1, 2, 3] | 1 0 2 0 3 0 |
Structs (S{int x, uint y} ) | cairo struct (S{Uint256 x, Uint256 y} ) | S(23, 45) | 23 0 45 0 |