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

ch02-11-starkli #244

Merged
merged 6 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading