Skip to content
This repository has been archived by the owner on Jun 20, 2024. It is now read-only.

Commit

Permalink
🎉 add ch02-11-starkli (#244)
Browse files Browse the repository at this point in the history
* Providers

* add starkli content

* fix broken links

* add ownable contract

* Add bash examples
  • Loading branch information
gianalarcon authored Nov 21, 2023
1 parent 1e37e7d commit b1e5f30
Show file tree
Hide file tree
Showing 10 changed files with 443 additions and 12 deletions.
2 changes: 2 additions & 0 deletions examples/Ownable-Starknet/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
.env
14 changes: 14 additions & 0 deletions examples/Ownable-Starknet/Scarb.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Code generated by scarb DO NOT EDIT.
version = 1

[[package]]
name = "ownable_starknet"
version = "0.1.0"
dependencies = [
"snforge_std",
]

[[package]]
name = "snforge_std"
version = "0.1.0"
source = "git+https://github.com/foundry-rs/starknet-foundry?tag=v0.7.1#d1bd8b9a361d437e8eaeb4ebffac291a48b4c920"
13 changes: 13 additions & 0 deletions examples/Ownable-Starknet/Scarb.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "ownable_starknet"
version = "0.1.0"

# See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest

[dependencies]
snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.7.1" } #v0.7.0
starknet = ">=2.2.0"

[[target.starknet-contract]]
sierra = true
casm = true
1 change: 1 addition & 0 deletions examples/Ownable-Starknet/data/calldata.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
'admin'
89 changes: 89 additions & 0 deletions examples/Ownable-Starknet/src/lib.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use core::traits::TryInto;
use starknet::ContractAddress;

#[starknet::interface]
trait IData<T> {
fn get_data(self: @T) -> felt252;
fn set_data(ref self: T, new_value: felt252);
fn other_func(self: @T, other_contract: ContractAddress) -> felt252;
}

#[starknet::interface]
trait OwnableTrait<T> {
fn transfer_ownership(ref self: T, new_owner: ContractAddress);
fn owner(self: @T) -> ContractAddress;
}

#[starknet::contract]
mod ownable {
use starknet::get_caller_address;
use super::{ContractAddress, IData, IDataDispatcherTrait, IDataDispatcher, OwnableTrait};

#[storage]
struct Storage {
owner: ContractAddress,
data: felt252,
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
OwnershipTransferred: OwnershipTransferred,
}

#[derive(Drop, starknet::Event)]
struct OwnershipTransferred {
#[key]
prev_owner: ContractAddress,
#[key]
new_owner: ContractAddress,
}

#[constructor]
fn constructor(ref self: ContractState, initial_owner: ContractAddress,) {
self.owner.write(initial_owner);
self.data.write(1);
// Any variable of the storage that is not initialized
// will have default value -> data = 0.
}

#[external(v0)]
impl OwnableDataImpl of IData<ContractState> {
fn other_func(self: @ContractState, other_contract: ContractAddress) -> felt252 {
IDataDispatcher { contract_address: other_contract }.get_data()
}

fn get_data(self: @ContractState) -> felt252 {
self.data.read()
}

fn set_data(ref self: ContractState, new_value: felt252) {
self.only_owner();
self.data.write(new_value);
}
}

#[external(v0)]
impl OwnableTraitImpl of OwnableTrait<ContractState> {
fn transfer_ownership(ref self: ContractState, new_owner: ContractAddress) {
self.only_owner();
let prev_owner = self.owner.read();
self.owner.write(new_owner);

self.emit(OwnershipTransferred { prev_owner, new_owner, });
}

fn owner(self: @ContractState) -> ContractAddress {
self.owner.read()
}
}

#[generate_trait]
impl PrivateMethods of PrivateMethodsTrait {
fn only_owner(self: @ContractState) {
let caller = get_caller_address();
assert(caller == self.owner.read(), 'Caller is not the owner');
}
}
}

87 changes: 87 additions & 0 deletions examples/Ownable-Starknet/tests/test_ownable.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use starknet::{ContractAddress, Into, TryInto, OptionTrait};
use starknet::syscalls::deploy_syscall;
use result::ResultTrait;
use array::{ArrayTrait, SpanTrait};
use snforge_std::{declare, ContractClassTrait};
use snforge_std::io::{FileTrait, read_txt};
use snforge_std::{start_prank, stop_prank};
use snforge_std::{start_mock_call, stop_mock_call};

use ownable_starknet::{OwnableTraitDispatcher, OwnableTraitDispatcherTrait};
use ownable_starknet::{IDataSafeDispatcher, IDataSafeDispatcherTrait};

mod Errors {
const INVALID_OWNER: felt252 = 'Caller is not the owner';
const INVALID_DATA: felt252 = 'Invalid data';
}

mod Accounts {
use traits::TryInto;
use starknet::ContractAddress;

fn admin() -> ContractAddress {
'admin'.try_into().unwrap()
}
fn new_admin() -> ContractAddress {
'new_admin'.try_into().unwrap()
}
fn bad_guy() -> ContractAddress {
'bad_guy'.try_into().unwrap()
}
}

fn deploy_contract(name: felt252) -> ContractAddress {
// let account = Accounts::admin();
let contract = declare(name);

let file = FileTrait::new('data/calldata.txt');
let calldata = read_txt(@file);
//deploy contract
contract.deploy(@calldata).unwrap()
}

#[test]
fn test_construct_with_admin() {
let contract_address = deploy_contract('ownable');
let dispatcher = OwnableTraitDispatcher { contract_address };
let owner = dispatcher.owner();
assert(owner == Accounts::admin(), Errors::INVALID_OWNER);
}

#[test]
fn test_transfer_ownership() {
let contract_address = deploy_contract('ownable');
let dispatcher = OwnableTraitDispatcher { contract_address };
start_prank(contract_address, Accounts::admin());
dispatcher.transfer_ownership(Accounts::new_admin());

assert(dispatcher.owner() == Accounts::new_admin(), Errors::INVALID_OWNER);
}

#[test]
#[should_panic(expected: ('Caller is not the owner',))]
fn test_transfer_ownership_bad_guy() {
let contract_address = deploy_contract('ownable');
let dispatcher = OwnableTraitDispatcher { contract_address };
start_prank(contract_address, Accounts::bad_guy());
dispatcher.transfer_ownership(Accounts::bad_guy());

assert(dispatcher.owner() == Accounts::bad_guy(), Errors::INVALID_OWNER);
}

#[test]
fn test_data_mock_call_get_data() {
let contract_address = deploy_contract('ownable');
let safe_dispatcher = IDataSafeDispatcher { contract_address };
let mock_ret_data = 100;
start_mock_call(contract_address, 'get_data', mock_ret_data);
start_prank(contract_address, Accounts::admin());
safe_dispatcher.set_data(20);
let data = safe_dispatcher.get_data().unwrap();
assert(data == mock_ret_data, Errors::INVALID_DATA);
stop_mock_call(contract_address, 'get_data');

let data2 = safe_dispatcher.get_data().unwrap();
assert(data2 == 20, Errors::INVALID_DATA);
stop_prank(contract_address);
}
10 changes: 10 additions & 0 deletions examples/script_devnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash
chain="$(starkli chain-id --rpc http://0.0.0.0:5050)"
echo "Where are connected to the starknet local devnet with chain id: $chain"

block="$(starkli block-number --rpc http://0.0.0.0:5050)"
echo "The latest block number on Katana is: $block"

account1="0x517ececd29116499f4a1b64b094da79ba08dfd54a3edaa316134c41f8160973"
balance="$(starkli balance $account1 --rpc http://0.0.0.0:5050)"
echo "The balance of account $account1 is: $balance eth"
15 changes: 15 additions & 0 deletions examples/script_testnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash

echo "input your testnet API URL: "
read url
chain="$(starkli chain-id --rpc $url)"
echo "Where are connected to the starknet testnet with chain id: $chain"

block="$(starkli block-number --rpc $url)"
echo "The latest block number on Goerli is: $block"

echo "input your transaction hash: "
read hash
receipt="$(starkli receipt $hash --rpc $url)"
echo "The receipt of transaction $hash is: $receipt"

20 changes: 8 additions & 12 deletions src/ch02-05-testnet-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This chapter guides developers through the process of compiling, deploying, and interacting with a Starknet smart contract written in Cairo on the testnet. Earlier, the focus was on deploying contracts using a local node, Katana. This time, the deployment and interaction target the Starknet testnet.

Ensure the following commands run successfully on your system. If not, see the 'Basic Installation' section:
Ensure the following commands run successfully on your system. If not, see the [Basic Installation](ch02-01-basic-installation.md) section:

```bash
scarb --version # For Cairo code compilation
Expand All @@ -17,8 +17,7 @@ the Account Descriptor is a JSON file detailing the wallet’s address and
public key.

In order for an account to be used as a signer it must be deployed to the appropriate network,
Starknet Goerli or mainnet, and funded. For this example we are going to use Goerli Testnet. To deploy your wallet, visit [Getting Started](ch01-00-getting-started.md) and find the `Smart Wallet Setup` section.

Starknet Goerli or mainnet, and funded. For this example we are going to use Goerli Testnet. To deploy your wallet, visit [Smart Wallet Setup](https://book.starknet.io/ch01-00-getting-started.html#smart-wallet-setup).
Now you’re ready to interact with Starknet smart contracts.

### Creating a Signer
Expand Down Expand Up @@ -203,7 +202,7 @@ In case you face an error like this:
Error: code=ContractNotFound, message="Contract with address {SMART_WALLET_ADDRESS} is not deployed."
```

It means you probably just created a new wallet and it has not been deployed yet. To accomplish this you have to fund your wallet with tokens and transfer tokens to a different wallet address. After this process, search your wallet address on the Starknet explorer. To see the details, go back to [Getting Started](ch01-00-getting-started.md) and find the `Smart Wallet Setup` section.
It means you probably just created a new wallet and it has not been deployed yet. To accomplish this you have to fund your wallet with tokens and transfer tokens to a different wallet address. After this process, search your wallet address on the Starknet explorer. To see the details, go back to [Smart Wallet Setup](https://book.starknet.io/ch01-00-getting-started.html#smart-wallet-setup).

After the acount descriptor file is generated, you can see its details, run:

Expand Down Expand Up @@ -259,17 +258,14 @@ efficient.
Deploying a smart contract on Starknet involves two steps:

- Declare your contract’s code.

- Deploy an instance of the declared code.

To get started, navigate to the `contracts/` directory in the [first
chapter](https://github.com/starknet-edu/starknetbook/tree/main/chapters/book/modules/chapter_1/pages/contracts)
To get started, navigate to the `src/` directory in the [Ownable-Starknet](https://github.com/starknet-edu/starknetbook/Ownable-Starknet) directory
of the Starknet Book repo. The `src/lib.cairo` file contains a basic
contract to practice with.

First, compile the contract using the Scarb compiler. If you haven’t
installed Scarb, follow the installation guide in the [Setting up your
Environment](https://book.starknet.io/chapter_1/environment_setup.html)
installed Scarb, follow the installation guide in the [basic instalation](./ch02-01-basic-installation)
section.

```bash
Expand Down Expand Up @@ -317,7 +313,7 @@ Run this command to declare your contract using the default Starknet
Sequencer’s Gateway:

```bash
starkli declare target/dev/contracts_Ownable.sierra.json
starkli declare target/dev/contracts_Ownable.contract_class.json
```

According to the `STARKNET_RPC` url, starkli can recognize the target
Expand Down Expand Up @@ -366,8 +362,8 @@ main components:
2. Any constructor arguments that the contract expects.

In our example, the constructor expects an _owner_ address. You can
learn more about constructors in \[Chapter 12 of The Cairo
Book\](<https://book.cairo-lang.org/ch99-01-03-02-contract-functions.html?highlight=constructor#1-constructors>).
learn more about constructors in [Chapter 12 of The Cairo
Book](https://book.cairo-lang.org/ch99-01-03-02-contract-functions.html?highlight=constructor#1-constructors).

The command would look like this:

Expand Down
Loading

0 comments on commit b1e5f30

Please sign in to comment.