Take advantage to use Starknet.js to deploy and use your new Cairo 1&2 smart-contracts.
The Cairo compiler provides an abi in the .sierra.json file. Today, this abi isn't fully mature, and some things are missing to have an handling similar to Cairo 0 contracts. Nevertheless, Starknet.js proposes a temporary solution for the early adopters of Cairo 1&2 smart-contracts.
It's realistic to hope to have a Cairo compiler providing a complete abi somewhere in
May/20232023.
You need to have :
- Starknet-devnet ^0.5.5 here.
- Cairo installed, from Starkware repo, branch
v2.0.0
here. - Starknet.js ^5.16.0 here.
We will use a small Cairo smart-contract, available here.
Go in your cairo directory, launch the compiler (adapt the path to your config):
cd cairo
cargo run --bin starknet-compile -- ../contracts/cairo200/PhilTest2.cairo ../out/cairo200/PhilTest2.sierra.json
cargo run --bin starknet-sierra-compile -- ../out/cairo200/PhilTest2.sierra.json ../out/cairo200/PhilTest2.casm.json
Launch starknet-devnet with this option :
starknet-devnet --seed 0
You can find 2 little scripts to deploy a contract :
The first script can be launched with :
npx ts-node src/scripts/cairo11-devnet/4.declareThenDeployTest.ts
You can easily change the network to use :
- Testnet :
const provider = new Provider({ sequencer: { network: constants.NetworkName.SN_GOERLI } });
- Testnet-2 :
const provider = new Provider({ sequencer: { network: constants.NetworkName.SN_GOERLI2 } });
And adapt in consequence the address and the private key to one of your account present in this network.
You can find this contract already deployed in testnet :
- Testnet address : 0x033de869eb1905fe503610527c51e245119bd05c231e7165c95d6fb630fe05ff
You can find a little script to interact with a Cairo 1 contract : here
Now, you have 2 ways to exchange data with Starknet :
bool, u8, u16, u32, usize, u64, u128, u256, ContractAddress are compatible.
Tuple, struct and array (made of literal types) are compatible.
In this case, use this code :
const res = await myTestContract.test1(100) // send a felt252
console.log("res1 =", res); // felt252 -> bigint
Complex objects, enums(Option
or custom), some mix of types are not yet compatible.
In this case, follow these rules (valid for all types, including compatible ones) :
Use CallData.compile() to prepare the parameters to send to the smart-contract.
Use only meta-class to interact with your contract (ex :
contract.getBalance()
)
For a @view function, do not forget these options for Cairo :
{
parseRequest: false,
parseResponse: false,
}
The answer is in an array of Hex numbers. ex :
result[0]
for the first value. An array returns the size, then the elements. An Uint256 returns 2 numbers (low,high).
Ex :
const par1 = CallData.compile({ balance: 100 })
const res1 = await myTestContract.test1(par1, { parseRequest: false, parseResponse: false, });
const tx = await myTestContract.increase_counter(
CallData.compile({
amount: 100,
})
);
console.log("res1 =", res1[0]); // Hex string
Debug.print() is not allowed in Cairo 1&2 code for Starknet network.
Hereafter is a way to debug anyway your Cairo contract, but a specific configuration is necessary :
You need a customized lib_funcs.json
file in your project.
The original file is in the Starkware repo : here.
You have to insert a line with "print",
, like this :
{
"allowed_libfuncs": [
"print",
"alloc_local",
"array_append",
"array_get",
"array_len",
...
My lib_funcs.json
is here.
We will test the print function with this small example (available here) :
#[external(v0)]
impl HelloContract of super::IHelloContract<ContractState> {
fn Say_HelloPhil126(ref self: ContractState, message: felt252) {
let caller = get_caller_address();
self.emit(Hello { from: caller, value: message }); // event
'Hello, Philippe!'.print();
123.print();
message.print();
}
}
Note the last lines, with the
.print()
command. The following commands are operational :
'Hello, Philippe!'.print();
123.print();
let a:u32 = 200;
a.print();
The compilation of the .cairo file needs some extra options :
cargo run --bin starknet-compile -- --allowed-libfuncs-list-file ./pathto/lib_funcs.json ./src/hello/hello.cairo ./out/hello/hello.sierra.json
Of course, adapt
pathto
to your config.
Same thing to compile the .sierra :
cargo run --bin starknet-sierra-compile -- --allowed-libfuncs-list-file ./pathto/lib_funcs.json ./out/hello/hello.sierra.json ../out/hello/hello.casm.json
Launch Starknet-devnet with these options :
starknet-devnet --seed 0 --compiler-args '--allowed-libfuncs-list-file ./pathto/lib_funcs.json --add-pythonic-hints' 2> /dev/null &
Note the PID of process, displayed in the first line (ex :
[1] 954825
). It will be necessary at the end of the process.
In the SAME bash window, launch the Starknet.js script that will deploy and interact with the contract (available here) :
npx ts-node src/scripts/cairo11-devnet/4c.declareAndDeployHello.ts
This script includes :
const th = await myTestContract.Say_HelloPhil126(200);
The following answer is printed in the bash window :
96231036770510887785732922765483205921
It can be decoded with this Starknet.js command :
console.log("Decoded message =", shortString.decodeShortString("96231036770510887785732922765483205921"));
(script available here)
And the answer is : Hello, Philippe!
Do not forget to kill the process of Devnet when you have ended to work with it (ex :
kill 954825
).
So, we can debug a Starknet Cairo 1 contract in Devnet with this process.
Do not hesitate to report your feedback and your questions in Discord.