From d8796265dc28af5a3cb01cf0f2f70fab2e693a87 Mon Sep 17 00:00:00 2001 From: JameWade Date: Mon, 8 Jan 2024 23:27:49 +0800 Subject: [PATCH 1/2] Add the starkli interaction process using katana as the local node --- src/SUMMARY.md | 1 + src/toolchain/katana/interact.md | 285 +++++++++++++++++++++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 src/toolchain/katana/interact.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index f8212436..8ab47409 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -62,6 +62,7 @@ - [events](./toolchain/sozo/world-commands/events.md) - [auth](./toolchain/sozo/world-commands/auth.md) - [Katana](./toolchain/katana/overview.md) + - [interact](./toolchain/katana/interact.md) - [Reference](./toolchain/katana/reference.md) - [Torii](./toolchain/torii/overview.md) - [Reference](./toolchain/torii/reference.md) diff --git a/src/toolchain/katana/interact.md b/src/toolchain/katana/interact.md new file mode 100644 index 00000000..1b73215f --- /dev/null +++ b/src/toolchain/katana/interact.md @@ -0,0 +1,285 @@ +### install starkli + +```console +curl https://get.starkli.sh | sh +## open a new terminal +starkliup +``` +You can check your installation by running `starkli --version`,then you will get the starkli version. +### install katana +```console +git clone https://github.com/dojoengine/dojo +cd dojo +cargo install --path ./crates/katana --locked --force +``` +You can check your installation by running `katana --version`,then you will get the katana version. +### Deploy and interact with contracts +#### run katana node +```console +katana --accounts 3 --seed 0 --gas-price 250 +``` +After starting the node, the accounts you need will be automatically generated and deployed. We need to import the pre-deployed accounts into starkli for use. +#### Configure starkli account +1. Configure the signer, execute the following command, and then enter the private key. This private key is the private key of the account generated by kanata. +```console +starkli signer keystore from-key ~/.starkli-wallets/deployer/account0_keystore.json +``` +2. Configure account description and create a new json file +```console +touch ~/.starkli-wallets/deployer/account0_account.json +``` +3. Copy the following content into the json file +```console +{ + "version": 1, + "variant": { + "type": "open_zeppelin", + "version": 1, + "public_key": "" + }, + "deployment": { + "status": "deployed", + "class_hash": "", + "address": "" + } +} +``` +Change `<>` and its contents to those provided by katana, including `public_key`, `class_hash`, and `address`.Among them, `class_hash` needs to be obtained with the following command. Please replace `` with address. +```console +starkli class-hash-at --rpc http://0.0.0.0:5050 +``` +In order to facilitate subsequent contract deployment and invocation, three accounts are deployed directly here. The process is the same.we will get 6 json files. +#### Contract Deployment +1. Create a Vote project +```console +## create a new project +scarb new vote +## Add contract dependencies to scarb.toml +[dependencies] +starknet = "2.1.0" +[[target.starknet-contract]] +casm = true +``` +Copy the vote contract to lib.cairo +```console +/// @dev Core Library Imports for the Traits outside the Starknet Contract +use starknet::ContractAddress; + +/// @dev Trait defining the functions that can be implemented or called by the Starknet Contract +#[starknet::interface] +trait VoteTrait { + /// @dev Function that returns the current vote status + fn get_vote_status(self: @T) -> (u8, u8, u8, u8); + /// @dev Function that checks if the user at the specified address is allowed to vote + fn voter_can_vote(self: @T, user_address: ContractAddress) -> bool; + /// @dev Function that checks if the specified address is registered as a voter + fn is_voter_registered(self: @T, address: ContractAddress) -> bool; + /// @dev Function that allows a user to vote + fn vote(ref self: T, vote: u8); +} + +/// @dev Starknet Contract allowing three registered voters to vote on a proposal +#[starknet::contract] +mod Vote { + use starknet::ContractAddress; + use starknet::get_caller_address; + + const YES: u8 = 1_u8; + const NO: u8 = 0_u8; + + /// @dev Structure that stores vote counts and voter states + #[storage] + struct Storage { + yes_votes: u8, + no_votes: u8, + can_vote: LegacyMap::, + registered_voter: LegacyMap::, + } + + /// @dev Contract constructor initializing the contract with a list of registered voters and 0 vote count + #[constructor] + fn constructor( + ref self: ContractState, + voter_1: ContractAddress, + voter_2: ContractAddress, + voter_3: ContractAddress + ) { + // Register all voters by calling the _register_voters function + self._register_voters(voter_1, voter_2, voter_3); + + // Initialize the vote count to 0 + self.yes_votes.write(0_u8); + self.no_votes.write(0_u8); + } + + /// @dev Event that gets emitted when a vote is cast + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + VoteCast: VoteCast, + UnauthorizedAttempt: UnauthorizedAttempt, + } + + /// @dev Represents a vote that was cast + #[derive(Drop, starknet::Event)] + struct VoteCast { + voter: ContractAddress, + vote: u8, + } + + /// @dev Represents an unauthorized attempt to vote + #[derive(Drop, starknet::Event)] + struct UnauthorizedAttempt { + unauthorized_address: ContractAddress, + } + + /// @dev Implementation of VoteTrait for ContractState + #[external(v0)] + impl VoteImpl of super::VoteTrait { + /// @dev Returns the voting results + fn get_vote_status(self: @ContractState) -> (u8, u8, u8, u8) { + let (n_yes, n_no) = self._get_voting_result(); + let (yes_percentage, no_percentage) = self._get_voting_result_in_percentage(); + (n_yes, n_no, yes_percentage, no_percentage) + } + + /// @dev Check whether a voter is allowed to vote + fn voter_can_vote(self: @ContractState, user_address: ContractAddress) -> bool { + self.can_vote.read(user_address) + } + + /// @dev Check whether an address is registered as a voter + fn is_voter_registered(self: @ContractState, address: ContractAddress) -> bool { + self.registered_voter.read(address) + } + + /// @dev Submit a vote + fn vote(ref self: ContractState, vote: u8) { + assert(vote == NO || vote == YES, 'VOTE_0_OR_1'); + let caller: ContractAddress = get_caller_address(); + self._assert_allowed(caller); + self.can_vote.write(caller, false); + + if (vote == NO) { + self.no_votes.write(self.no_votes.read() + 1_u8); + } + if (vote == YES) { + self.yes_votes.write(self.yes_votes.read() + 1_u8); + } + + self.emit(VoteCast { voter: caller, vote: vote, }); + } + } + + /// @dev Internal Functions implementation for the Vote contract + #[generate_trait] + impl InternalFunctions of InternalFunctionsTrait { + /// @dev Registers the voters and initializes their voting status to true (can vote) + fn _register_voters( + ref self: ContractState, + voter_1: ContractAddress, + voter_2: ContractAddress, + voter_3: ContractAddress + ) { + self.registered_voter.write(voter_1, true); + self.can_vote.write(voter_1, true); + + self.registered_voter.write(voter_2, true); + self.can_vote.write(voter_2, true); + + self.registered_voter.write(voter_3, true); + self.can_vote.write(voter_3, true); + } + } + + /// @dev Asserts implementation for the Vote contract + #[generate_trait] + impl AssertsImpl of AssertsTrait { + // @dev Internal function that checks if an address is allowed to vote + fn _assert_allowed(ref self: ContractState, address: ContractAddress) { + let is_voter: bool = self.registered_voter.read((address)); + let can_vote: bool = self.can_vote.read((address)); + + if (can_vote == false) { + self.emit(UnauthorizedAttempt { unauthorized_address: address, }); + } + + assert(is_voter == true, 'USER_NOT_REGISTERED'); + assert(can_vote == true, 'USER_ALREADY_VOTED'); + } + } + + /// @dev Implement the VotingResultTrait for the Vote contract + #[generate_trait] + impl VoteResultFunctionsImpl of VoteResultFunctionsTrait { + // @dev Internal function to get the voting results (yes and no vote counts) + fn _get_voting_result(self: @ContractState) -> (u8, u8) { + let n_yes: u8 = self.yes_votes.read(); + let n_no: u8 = self.no_votes.read(); + + (n_yes, n_no) + } + + // @dev Internal function to calculate the voting results in percentage + fn _get_voting_result_in_percentage(self: @ContractState) -> (u8, u8) { + let n_yes: u8 = self.yes_votes.read(); + let n_no: u8 = self.no_votes.read(); + + let total_votes: u8 = n_yes + n_no; + + if (total_votes == 0_u8) { + return (0, 0); + } + let yes_percentage: u8 = (n_yes * 100_u8) / (total_votes); + let no_percentage: u8 = (n_no * 100_u8) / (total_votes); + + (yes_percentage, no_percentage) + } + } +} +``` +2. Compile contract +```console +scarb build +``` +3. Declare contract +```console +starkli declare target/dev/test_Vote.sierra.json --compiler-version 2.1.0 --rpc http://0.0.0.0:5050 --account ~/.starkli-wallets/deployer/account0_account.json --keystore ~/.starkli-wallets/deployer/account0_keystore.json +``` +4. Deploy contract +```console +## starkli deploy --rpc http://0.0.0.0:5050 --account ~/.starkli-wallets/deployer/account0_account.json --keystore ~/.starkli-wallets/deployer/account0_keystore.json +## There are four hexadecimal numbers in total. The first one is the class_hash of the contract, and the next three are the vote account addresses, which are the three account addresses in the previous account configuration. +## Below is the command I ran,you need to change it to your address +starkli deploy 0x043e965f6c644b15c0c46d5615c22cf26746edc2547f482ae5ab517d3dffdf37 0x517ececd29116499f4a1b64b094da79ba08dfd54a3edaa316134c41f8160973 0x5686a647a9cdd63ade617e0baf3b364856b813b508f03903eb58a7e622d5855 0x765149d6bc63271df7b0316537888b81aa021523f9516a05306f10fd36914da --rpc http://0.0.0.0:5050 --account ~/.starkli-wallets/deployer/account0_account.json --keystore ~/.starkli-wallets/deployer/account0_keystore.json +``` +5. Call contract [only read state] +```console +## The first parameter is the contract address, the second parameter is the function to be called, and the third parameter is the function parameter. Here I pass in a voting address. +starkli call 0x050ffb64b3042bf91422dfa1453b3cdeaf4af7eca8562b499fc49017d41b85ec voter_can_vote 0x517ececd29116499f4a1b64b094da79ba08dfd54a3edaa316134c41f8160973 --rpc http://0.0.0.0:5050 +``` +6. Invoke contract [can write state] +```console +##The first parameter is the contract address, the second parameter is the function to be invoked, and the third parameter is the function parameter +##Voting Yes +starkli invoke 0x050ffb64b3042bf91422dfa1453b3cdeaf4af7eca8562b499fc49017d41b85ec vote 1 --rpc http://0.0.0.0:5050 --account ~/.starkli-wallets/deployer/account0_account.json --keystore ~/.starkli-wallets/deployer/account0_keystore.json + +##Voting No +starkli invoke 0x050ffb64b3042bf91422dfa1453b3cdeaf4af7eca8562b499fc49017d41b85ec vote 0 --rpc http://0.0.0.0:5050 --account ~/.starkli-wallets/deployer/account1_account.json --keystore ~/.starkli-wallets/deployer/account1_keystore.json + +##The same signer cannot vote repeatedly. If the same signer votes repeatedly, Katana will report an error. +called contract (0x0517ececd29116499f4a1b64b094da79ba08dfd54a3edaa316134c41f8160973): +Error at pc=0:81: +Got an exception while executing a hint: Custom Hint Error: Execution failed. Failure reason: \"USER_ALREADY_VOTED\". +Cairo traceback (most recent call last): +Unknown location (pc=0:731) +Unknown location (pc=0:677) +Unknown location (pc=0:291) +Unknown location (pc=0:314) +``` +7. Query transaction +```console +### starkli transaction --rpc http://0.0.0.0:5050 +starkli transaction 0x0499bca29c88798ea70233c41d1d17621f49a2afb1f4ed902ca2eecf4a19e951 --rpc http://0.0.0.0:5050 +``` +All the above interaction processes can be seen on the katana client. Pay attention to the status changes of katana at each step. From 1596345b81eb91618b3feea24f95a037eac98fb3 Mon Sep 17 00:00:00 2001 From: JameWade Date: Sat, 13 Jan 2024 15:58:56 +0800 Subject: [PATCH 2/2] Add the main work of this section and the introduction of starkli --- src/toolchain/katana/interact.md | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/toolchain/katana/interact.md b/src/toolchain/katana/interact.md index 1b73215f..86e8146d 100644 --- a/src/toolchain/katana/interact.md +++ b/src/toolchain/katana/interact.md @@ -5,30 +5,45 @@ curl https://get.starkli.sh | sh ## open a new terminal starkliup ``` + You can check your installation by running `starkli --version`,then you will get the starkli version. + ### install katana + ```console git clone https://github.com/dojoengine/dojo cd dojo cargo install --path ./crates/katana --locked --force ``` + You can check your installation by running `katana --version`,then you will get the katana version. + ### Deploy and interact with contracts + #### run katana node + ```console katana --accounts 3 --seed 0 --gas-price 250 ``` + After starting the node, the accounts you need will be automatically generated and deployed. We need to import the pre-deployed accounts into starkli for use. + #### Configure starkli account + 1. Configure the signer, execute the following command, and then enter the private key. This private key is the private key of the account generated by kanata. + ```console starkli signer keystore from-key ~/.starkli-wallets/deployer/account0_keystore.json ``` + 2. Configure account description and create a new json file + ```console touch ~/.starkli-wallets/deployer/account0_account.json ``` + 3. Copy the following content into the json file + ```console { "version": 1, @@ -44,13 +59,19 @@ touch ~/.starkli-wallets/deployer/account0_account.json } } ``` + Change `<>` and its contents to those provided by katana, including `public_key`, `class_hash`, and `address`.Among them, `class_hash` needs to be obtained with the following command. Please replace `` with address. + ```console starkli class-hash-at --rpc http://0.0.0.0:5050 ``` + In order to facilitate subsequent contract deployment and invocation, three accounts are deployed directly here. The process is the same.we will get 6 json files. + #### Contract Deployment + 1. Create a Vote project + ```console ## create a new project scarb new vote @@ -60,7 +81,9 @@ starknet = "2.1.0" [[target.starknet-contract]] casm = true ``` + Copy the vote contract to lib.cairo + ```console /// @dev Core Library Imports for the Traits outside the Starknet Contract use starknet::ContractAddress; @@ -238,27 +261,37 @@ mod Vote { } } ``` + 2. Compile contract + ```console scarb build ``` + 3. Declare contract + ```console starkli declare target/dev/test_Vote.sierra.json --compiler-version 2.1.0 --rpc http://0.0.0.0:5050 --account ~/.starkli-wallets/deployer/account0_account.json --keystore ~/.starkli-wallets/deployer/account0_keystore.json ``` + 4. Deploy contract + ```console ## starkli deploy --rpc http://0.0.0.0:5050 --account ~/.starkli-wallets/deployer/account0_account.json --keystore ~/.starkli-wallets/deployer/account0_keystore.json ## There are four hexadecimal numbers in total. The first one is the class_hash of the contract, and the next three are the vote account addresses, which are the three account addresses in the previous account configuration. ## Below is the command I ran,you need to change it to your address starkli deploy 0x043e965f6c644b15c0c46d5615c22cf26746edc2547f482ae5ab517d3dffdf37 0x517ececd29116499f4a1b64b094da79ba08dfd54a3edaa316134c41f8160973 0x5686a647a9cdd63ade617e0baf3b364856b813b508f03903eb58a7e622d5855 0x765149d6bc63271df7b0316537888b81aa021523f9516a05306f10fd36914da --rpc http://0.0.0.0:5050 --account ~/.starkli-wallets/deployer/account0_account.json --keystore ~/.starkli-wallets/deployer/account0_keystore.json ``` + 5. Call contract [only read state] + ```console ## The first parameter is the contract address, the second parameter is the function to be called, and the third parameter is the function parameter. Here I pass in a voting address. starkli call 0x050ffb64b3042bf91422dfa1453b3cdeaf4af7eca8562b499fc49017d41b85ec voter_can_vote 0x517ececd29116499f4a1b64b094da79ba08dfd54a3edaa316134c41f8160973 --rpc http://0.0.0.0:5050 ``` + 6. Invoke contract [can write state] + ```console ##The first parameter is the contract address, the second parameter is the function to be invoked, and the third parameter is the function parameter ##Voting Yes @@ -277,9 +310,12 @@ Unknown location (pc=0:677) Unknown location (pc=0:291) Unknown location (pc=0:314) ``` + 7. Query transaction + ```console ### starkli transaction --rpc http://0.0.0.0:5050 starkli transaction 0x0499bca29c88798ea70233c41d1d17621f49a2afb1f4ed902ca2eecf4a19e951 --rpc http://0.0.0.0:5050 ``` + All the above interaction processes can be seen on the katana client. Pay attention to the status changes of katana at each step.