From 0abf03af76e4a3524db1499b97dcbb01c786a160 Mon Sep 17 00:00:00 2001 From: cryptonerdcn Date: Thu, 7 Dec 2023 00:11:22 +0900 Subject: [PATCH 1/2] Recover zh-cn.po --- po/zh-cn.po | 10747 +++++++++++++++++++++++--------------------------- 1 file changed, 5019 insertions(+), 5728 deletions(-) diff --git a/po/zh-cn.po b/po/zh-cn.po index 09ed2d4a..aedc9584 100644 --- a/po/zh-cn.po +++ b/po/zh-cn.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: Starknet by Example\n" -"POT-Creation-Date: 2023-12-06T08:37:01Z\n" +"POT-Creation-Date: \n" "PO-Revision-Date: 2023-12-05 18:26+0900\n" "Last-Translator: StarknetAstro \n" "Language-Team: Language zh-cn\n" @@ -12,10 +12,6 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Poedit 3.4.1\n" -#: src/SUMMARY.md:1 -msgid "Summary" -msgstr "" - #: src/SUMMARY.md:3 msgid "Introduction" msgstr "介绍" @@ -28,19 +24,19 @@ msgstr "入门" msgid "Basics of a Starknet contract" msgstr "Starknet合约的基本内容" -#: src/SUMMARY.md:9 src/ch00/basics/storage.md:1 src/ch00/basics/variables.md:8 +#: src/SUMMARY.md:9 msgid "Storage" msgstr "存储" -#: src/SUMMARY.md:10 src/ch00/basics/constructor.md:1 +#: src/SUMMARY.md:10 msgid "Constructor" msgstr "构造函数" -#: src/SUMMARY.md:11 src/ch00/basics/variables.md:1 +#: src/SUMMARY.md:11 msgid "Variables" msgstr "变量" -#: src/SUMMARY.md:12 src/ch00/basics/visibility-mutability.md:1 +#: src/SUMMARY.md:12 msgid "Visibility and Mutability" msgstr "可见性和可变性" @@ -48,39 +44,39 @@ msgstr "可见性和可变性" msgid "Counter Example" msgstr "计数器示例" -#: src/SUMMARY.md:14 src/ch00/basics/mappings.md:1 +#: src/SUMMARY.md:14 msgid "Mappings" msgstr "映射" -#: src/SUMMARY.md:15 src/ch00/basics/errors.md:1 +#: src/SUMMARY.md:15 msgid "Errors" msgstr "错误" -#: src/SUMMARY.md:16 src/ch00/basics/events.md:1 +#: src/SUMMARY.md:16 msgid "Events" msgstr "事件" -#: src/SUMMARY.md:17 src/ch00/basics/storing-custom-types.md:1 +#: src/SUMMARY.md:17 msgid "Storing Custom Types" msgstr "存储自定义类型" -#: src/SUMMARY.md:18 src/ch00/basics/custom-types-in-entrypoints.md:1 +#: src/SUMMARY.md:18 msgid "Custom types in entrypoints" msgstr "入口点中的自定义类型" -#: src/SUMMARY.md:19 src/ch00/basics/documentation.md:1 +#: src/SUMMARY.md:19 msgid "Documentation" msgstr "文档" -#: src/SUMMARY.md:20 src/ch00/interacting/interacting.md:1 +#: src/SUMMARY.md:20 msgid "Deploy and interact with contracts" msgstr "部署合约并与合约交互" -#: src/SUMMARY.md:21 src/ch00/interacting/interfaces-traits.md:1 +#: src/SUMMARY.md:21 msgid "Contract interfaces and Traits generation" msgstr "合约接口和Trait生成" -#: src/SUMMARY.md:22 src/ch00/interacting/calling_other_contracts.md:1 +#: src/SUMMARY.md:22 msgid "Calling other contracts" msgstr "调用其他合约" @@ -104,27 +100,27 @@ msgstr "Felt" msgid "LegacyMap" msgstr "LegacyMap" -#: src/SUMMARY.md:28 src/ch00/cairo_cheatsheet/arrays.md:1 +#: src/SUMMARY.md:28 msgid "Arrays" msgstr "数组" -#: src/SUMMARY.md:29 src/ch00/cairo_cheatsheet/loop.md:1 +#: src/SUMMARY.md:29 msgid "Loop" msgstr "循环" -#: src/SUMMARY.md:30 src/ch00/cairo_cheatsheet/match.md:1 +#: src/SUMMARY.md:30 msgid "Match" msgstr "匹配" -#: src/SUMMARY.md:31 src/ch00/cairo_cheatsheet/tuples.md:1 +#: src/SUMMARY.md:31 msgid "Tuples" msgstr "元组" -#: src/SUMMARY.md:32 src/ch00/cairo_cheatsheet/struct.md:1 +#: src/SUMMARY.md:32 msgid "Struct" msgstr "结构体" -#: src/SUMMARY.md:33 src/ch00/cairo_cheatsheet/type_casting.md:1 +#: src/SUMMARY.md:33 msgid "Type casting" msgstr "类型转换" @@ -132,7 +128,7 @@ msgstr "类型转换" msgid "Applications examples" msgstr "应用实例" -#: src/SUMMARY.md:37 src/ch01/upgradeable_contract.md:1 +#: src/SUMMARY.md:37 msgid "Upgradeable Contract" msgstr "可升级合约" @@ -140,11 +136,11 @@ msgstr "可升级合约" msgid "Defi Vault" msgstr "Defi Vault" -#: src/SUMMARY.md:39 src/ch01/erc20.md:1 +#: src/SUMMARY.md:39 msgid "ERC20 Token" msgstr "ERC20 代币" -#: src/SUMMARY.md:40 src/ch01/constant-product-amm.md:1 +#: src/SUMMARY.md:40 msgid "Constant Product AMM" msgstr "恒定产品 AMM" @@ -152,11 +148,11 @@ msgstr "恒定产品 AMM" msgid "Advanced concepts" msgstr "高级概念" -#: src/SUMMARY.md:44 src/ch02/write_to_any_slot.md:1 +#: src/SUMMARY.md:44 msgid "Writing to any storage slot" msgstr "写入任何存储槽" -#: src/SUMMARY.md:45 src/ch02/storing_arrays.md:1 +#: src/SUMMARY.md:45 msgid "Storing Arrays" msgstr "存储数组" @@ -164,11 +160,11 @@ msgstr "存储数组" msgid "Struct as mapping key" msgstr "结构体作为映射键" -#: src/SUMMARY.md:47 src/ch02/hash-solidity-compatible.md:1 +#: src/SUMMARY.md:47 msgid "Hash Solidity Compatible" msgstr "兼容Hash Solidity" -#: src/SUMMARY.md:48 src/ch02/optimisations/optimisations.md:1 +#: src/SUMMARY.md:48 msgid "Optimisations" msgstr "优化" @@ -176,13 +172,12 @@ msgstr "优化" msgid "Storage Optimisations" msgstr "存储优化" -#: src/SUMMARY.md:50 src/ch02/list.md:1 +#: src/SUMMARY.md:50 msgid "List" msgstr "列表" #: src/starknet-by-example.md:1 -#, fuzzy -msgid "Starknet by Example" +msgid "# Starknet by Example" msgstr "# Starknet by Example" #: src/starknet-by-example.md:3 @@ -190,8 +185,7 @@ msgid "" "Starknet By Example is a collection of examples of how to use the Cairo " "programming language to create smart contracts on Starknet." msgstr "" -"Starknet By Example是如何使用Cairo编程语言在Starknet上创建智能合约的范例" -"集。\n" +"Starknet By Example是如何使用Cairo编程语言在Starknet上创建智能合约的范例集。\n" "中文版由 [StarknetAstro](https://twitter.com/StarkNetAstroCN) 社区翻译。" #: src/starknet-by-example.md:5 @@ -205,20 +199,19 @@ msgstr "" #: src/starknet-by-example.md:7 msgid "" -"Starknet smart contracts are written in the Cairo language. Cairo is a " -"Turing-complete programming language designed to write provable programs, " +"Starknet smart contracts are written in the Cairo language. Cairo is a Turing-" +"complete programming language designed to write provable programs, " "abstracting the zk-STARK proof system away from the programmer." msgstr "" -"Starknet智能合约是用Cairo语言编写的。Cairo语言是一种图灵完备的编程语言,旨在" -"编写可证明的程序,将 zk-STARK 证明系统从程序员手中抽象出来。" +"Starknet智能合约是用Cairo语言编写的。Cairo语言是一种图灵完备的编程语言,旨在编" +"写可证明的程序,将 zk-STARK 证明系统从程序员手中抽象出来。" #: src/starknet-by-example.md:9 msgid "The current version of this book use `scarb 2.3.1`" msgstr "本书当前版本使用 `scarb 2.3.1`" #: src/starknet-by-example.md:11 -#, fuzzy -msgid "For whom is this for?" +msgid "## For whom is this for?" msgstr "## 谁该读这本书?" #: src/starknet-by-example.md:13 @@ -227,33 +220,32 @@ msgid "" "smart contracts on Starknet using Cairo with some technical background in " "programming and blockchain." msgstr "" -"Starknet By Example适合想要快速学习如何使用 Cairo 在 Starknet 上编写智能合" -"约,并具有一定编程和区块链技术背景的人。" +"Starknet By Example适合想要快速学习如何使用 Cairo 在 Starknet 上编写智能合约," +"并具有一定编程和区块链技术背景的人。" #: src/starknet-by-example.md:15 -#, fuzzy msgid "" "The first chapters will give you a basic understanding of the Cairo " "programming language and how to write, deploy and use smart contracts on " -"Starknet. The later chapters will cover more advanced topics and show you " -"how to write more complex smart contracts." +"Starknet.\n" +"The later chapters will cover more advanced topics and show you how to write " +"more complex smart contracts." msgstr "" "前几章将让你基本了解Cairo编程语言,以及如何在Starknet编写、部署和使用智能合" "约。\n" "后面的章节将涉及更高级的主题,并向你展示如何编写更复杂的智能合约。" #: src/starknet-by-example.md:18 -#, fuzzy -msgid "Further reading" +msgid "## Further reading" msgstr "## 进一步阅读" #: src/starknet-by-example.md:20 -#, fuzzy msgid "" "If you want to learn more about the Cairo programming language, you can read " -"the [Cairo Book](https://book.cairo-lang.org). If you want to learn more " -"about Starknet, you can read the [Starknet documentation](https://docs." -"starknet.io/) and the [Starknet Book](https://book.starknet.io)." +"the [Cairo Book](https://book.cairo-lang.org).\n" +"If you want to learn more about Starknet, you can read the [Starknet " +"documentation](https://docs.starknet.io/) and the [Starknet Book](https://" +"book.starknet.io)." msgstr "" "如果你想进一步了解 Cairo 编程语言,可以阅读[Cairo Book](https://book.cairo-" "lang.org/zh-cn/index.html)。\n" @@ -266,43 +258,81 @@ msgstr "以下是您可能会用到的其他资源清单:" #: src/starknet-by-example.md:24 msgid "" -"[Starklings](https://github.com/shramee/starklings-cairo1): An interactive " -"tutorial to get you up and running with Cairo v1 and Starknet " -msgstr "" - -#: src/starknet-by-example.md:25 -msgid "" -"[Cairopractice](https://cairopractice.com/): A blog with a series of " -"articles about Cairo and Starknet" -msgstr "" - -#: src/starknet-by-example.md:26 -msgid "" -"[Cairo by example](https://cairo-by-example.com/): An introduction to Cairo, " -"with simple examples" -msgstr "" +"- [Starklings](https://github.com/shramee/starklings-cairo1): An interactive " +"tutorial to get you up and running with Cairo v1 and Starknet \n" +"- [Cairopractice](https://cairopractice.com/): A blog with a series of " +"articles about Cairo and Starknet\n" +"- [Cairo by example](https://cairo-by-example.com/): An introduction to " +"Cairo, with simple examples" +msgstr "" +"- [Starklings](https://github.com/shramee/starklings-cairo1):让您使用 Cairo " +"v1 和Starknet互动的教程\n" +"- [Cairopractice](https://cairopractice.com/):关于Cairo和Starknet的一系列文章" +"的博客\n" +"- [Cairo by example](https://cairo-by-example.com/):Cairo 简介,附带简单示例" + +#: src/starknet-by-example.md:28 +#: src/ch00/interacting/calling_other_contracts.md:69 +#: src/ch00/cairo_cheatsheet/felt.md:15 src/ch00/cairo_cheatsheet/loop.md:23 +#: src/ch00/cairo_cheatsheet/tuples.md:18 +#: src/ch00/cairo_cheatsheet/type_casting.md:33 +msgid "
Last change: 2023-11-30
" +msgstr "
Last change: 2023-11-30
" #: src/ch00/basics/introduction.md:1 -#, fuzzy -msgid "Basics of Smart Contracts in Cairo" +msgid "# Basics of Smart Contracts in Cairo" msgstr "# Cairo的智能合约基础知识" #: src/ch00/basics/introduction.md:3 msgid "" -"The following chapters will introduce you to Starknet smart contracts and " -"how to write them in Cairo." +"The following chapters will introduce you to Starknet smart contracts and how " +"to write them in Cairo." msgstr "以下章节将向你介绍Starknet智能合约以及如何用Cairo编写这些合约。" +#: src/ch00/basics/introduction.md:5 src/ch00/basics/constructor.md:27 +#: src/ch00/basics/mappings.md:46 src/ch00/basics/errors.md:139 +#: src/ch00/basics/events.md:68 src/ch00/basics/storing-custom-types.md:39 +#: src/ch00/basics/custom-types-in-entrypoints.md:39 +#: src/ch00/testing/contract-testing.md:176 src/ch01/upgradeable_contract.md:117 +#: src/ch01/simple_vault.md:114 src/ch02/write_to_any_slot.md:58 +#: src/ch02/storing_arrays.md:105 src/ch02/struct-mapping-key.md:46 +#: src/ch02/optimisations/optimisations.md:5 +#: src/ch02/optimisations/store_using_packing.md:90 +msgid "
Last change: 2023-10-12
" +msgstr "
Last change: 2023-10-12
" + +#: src/ch00/basics/storage.md:1 +msgid "# Storage" +msgstr "# 存储" + #: src/ch00/basics/storage.md:3 msgid "Here's the most minimal contract you can write in Cairo:" msgstr "这是您用Cairo能写的最简短的合约:" +#: src/ch00/basics/storage.md:5 +msgid "" +"```rust\n" +"#[starknet::contract]\n" +"mod Contract {\n" +" #[storage]\n" +" struct Storage {}\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"#[starknet::contract]\n" +"mod Contract {\n" +" #[storage]\n" +" struct Storage {}\n" +"}\n" +"```" + #: src/ch00/basics/storage.md:13 -#, fuzzy msgid "" "Storage is a struct annoted with `#[storage]`. Every contract must have one " -"and only one storage. It's a key-value store, where each key will be mapped " -"to a storage address of the contract's storage space." +"and only one storage.\n" +"It's a key-value store, where each key will be mapped to a storage address of " +"the contract's storage space." msgstr "" "存储是一个结构体,用 `#[storage]`标注。每个合约必须有且仅有一个存储空间。\n" "它是一个键值存储空间,其中每个键都将映射到合约存储空间的存储地址。" @@ -315,12 +345,37 @@ msgstr "" "您可以在合约中定义 [存储变量](./variables.md#storage-variables),然后使用它" "们来存储和检索数据。" +#: src/ch00/basics/storage.md:17 +msgid "" +"```rust\n" +"#[starknet::contract]\n" +"mod Contract {\n" +" #[storage]\n" +" struct Storage {\n" +" a: u128,\n" +" b: u8,\n" +" c: u256\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"#[starknet::contract]\n" +"mod Contract {\n" +" #[storage]\n" +" struct Storage {\n" +" a: u128,\n" +" b: u8,\n" +" c: u256\n" +" }\n" +"}\n" +"```" + #: src/ch00/basics/storage.md:29 -#, fuzzy msgid "" -"Actually these two contracts have the same underlying sierra program. From " -"the compiler's perspective, the storage variables don't exist until they are " -"used." +"> Actually these two contracts have the same underlying sierra program.\n" +"> From the compiler's perspective, the storage variables don't exist until " +"they are used." msgstr "" "> 实际上,这两个合约的底层 sierra 程序是一样的。\n" "> 从编译器的角度来看,存储变量在使用之前是不存在的。" @@ -330,6 +385,15 @@ msgid "" "You can also read about [storing custom types](./storing-custom-types.md)" msgstr "您还可以阅读有关 [存储自定义类型](./storing-custom-types.md) 的内容。" +#: src/ch00/basics/storage.md:34 src/ch00/basics/variables.md:126 +#: src/ch01/constant-product-amm.md:275 +msgid "
Last change: 2023-11-20
" +msgstr "
Last change: 2023-11-20
" + +#: src/ch00/basics/constructor.md:1 +msgid "# Constructor" +msgstr "# 构造函数" + #: src/ch00/basics/constructor.md:3 msgid "" "Constructors are a special type of function that runs only once when " @@ -338,23 +402,59 @@ msgid "" "constructor function must be annotated with the `#[constructor]` attribute. " "Also, a good practice consists in naming that function `constructor`." msgstr "" -"构造函数是一种特殊类型的函数,只在部署合约时运行一次,可用于初始化合约的状" -"态。你的合约不能有一个以上的构造函数,而且构造函数必须使用 `#[constructor]` " -"属性注释。此外,一个好的做法是将该函数命名为 `constructor`。" +"构造函数是一种特殊类型的函数,只在部署合约时运行一次,可用于初始化合约的状态。" +"你的合约不能有一个以上的构造函数,而且构造函数必须使用 `#[constructor]` 属性注" +"释。此外,一个好的做法是将该函数命名为 `constructor`。" #: src/ch00/basics/constructor.md:5 msgid "" "Here's a simple example that demonstrates how to initialize the state of a " "contract on deployment by defining logic inside a constructor." msgstr "" -"下面是一个简单的示例,演示如何通过在构造函数中定义逻辑,在部署时初始化合约的" -"状态。" +"下面是一个简单的示例,演示如何通过在构造函数中定义逻辑,在部署时初始化合约的状" +"态。" -#: src/ch00/basics/constructor.md:17 +#: src/ch00/basics/constructor.md:7 msgid "" -"// The constructor is decorated with a `#[constructor]` attribute.\n" +"```rust\n" +"#[starknet::contract]\n" +"mod ExampleConstructor {\n" +" use starknet::ContractAddress;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" names: LegacyMap::,\n" +" }\n" +"\n" +" // The constructor is decorated with a `#[constructor]` attribute.\n" " // It is not inside an `impl` block.\n" +" #[constructor]\n" +" fn constructor(ref self: ContractState, name: felt252, address: " +"ContractAddress) {\n" +" self.names.write(address, name);\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"#[starknet::contract]\n" +"mod ExampleConstructor {\n" +" use starknet::ContractAddress;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" names: LegacyMap::,\n" +" }\n" +"\n" +" // The constructor is decorated with a `#[constructor]` attribute.\n" +" // It is not inside an `impl` block.\n" +" #[constructor]\n" +" fn constructor(ref self: ContractState, name: felt252, address: " +"ContractAddress) {\n" +" self.names.write(address, name);\n" +" }\n" +"}\n" +"```" #: src/ch00/basics/constructor.md:25 msgid "" @@ -371,55 +471,48 @@ msgstr "" "blob/main/listings/ch00-getting-started/constructor/src/constructor.cairo) 中" "尝试它。" +#: src/ch00/basics/variables.md:1 +msgid "# Variables" +msgstr "# 变量" + #: src/ch00/basics/variables.md:3 msgid "There are 3 types of variables in Cairo contracts:" msgstr "Cairo合约中有 3 种变量:" #: src/ch00/basics/variables.md:5 -msgid "Local" -msgstr "" - -#: src/ch00/basics/variables.md:6 -msgid "declared inside a function" -msgstr "" - -#: src/ch00/basics/variables.md:7 -msgid "not stored on the blockchain" -msgstr "" - -#: src/ch00/basics/variables.md:9 -msgid "declared in the [Storage](./storage.md) of a contract" -msgstr "" - -#: src/ch00/basics/variables.md:10 -msgid "can be accessed from one execution to another" -msgstr "" - -#: src/ch00/basics/variables.md:11 -msgid "Global" -msgstr "" - -#: src/ch00/basics/variables.md:12 -msgid "provides information about the blockchain" -msgstr "" - -#: src/ch00/basics/variables.md:13 -msgid "accessed anywhere, even within library functions" -msgstr "" +msgid "" +"- Local\n" +" - declared inside a function\n" +" - not stored on the blockchain\n" +"- Storage\n" +" - declared in the [Storage](./storage.md) of a contract\n" +" - can be accessed from one execution to another\n" +"- Global\n" +" - provides information about the blockchain\n" +" - accessed anywhere, even within library functions" +msgstr "" +"- 局部\n" +" - 在函数中声明\n" +" - 不存储在区块链中\n" +"- 存储\n" +" - 在合约的 [Storage](./storage.md) 中声明\n" +" - 可从一个执行过程访问到另一个执行过程\n" +"- 全局\n" +" - 提供有关区块链的信息\n" +" - 可在任何地方访问,甚至在库函数中" #: src/ch00/basics/variables.md:15 -#, fuzzy -msgid "Local Variables" +msgid "## Local Variables" msgstr "## 局部变量" #: src/ch00/basics/variables.md:17 msgid "" -"Local variables are used and accessed within the scope of a specific " -"function or block of code. They are temporary and exist only for the " -"duration of that particular function or block execution." +"Local variables are used and accessed within the scope of a specific function " +"or block of code. They are temporary and exist only for the duration of that " +"particular function or block execution." msgstr "" -"局部变量在特定函数或代码块的范围内使用和访问。它们是临时的,只在特定函数或代" -"码块执行期间存在。" +"局部变量在特定函数或代码块的范围内使用和访问。它们是临时的,只在特定函数或代码" +"块执行期间存在。" #: src/ch00/basics/variables.md:19 msgid "" @@ -429,25 +522,77 @@ msgid "" "a specific context. They also make the code more readable by giving names to " "intermediate values." msgstr "" -"局部变量存储在内存中,不会存储在区块链上。这就意味着在执行过程中无法访问它" -"们。局部变量可用于存储仅在特定上下文中相关的临时数据。通过为中间值命名,它们" -"还能使代码更具可读性。" +"局部变量存储在内存中,不会存储在区块链上。这就意味着在执行过程中无法访问它们。" +"局部变量可用于存储仅在特定上下文中相关的临时数据。通过为中间值命名,它们还能使" +"代码更具可读性。" #: src/ch00/basics/variables.md:21 msgid "Here's a simple example of a contract with only local variables:" msgstr "下面是一个只有局部变量的简单合约示例:" -#: src/ch00/basics/variables.md:37 -msgid "" -"// This variable is local to the current block. It can't be accessed once it " -"goes out of scope.\n" -msgstr "" - -#: src/ch00/basics/variables.md:41 +#: src/ch00/basics/variables.md:23 msgid "" -"// The scope of a code block allows for local variable declaration\n" +"```rust\n" +"#[starknet::interface]\n" +"trait ILocalVariablesExample {\n" +" fn do_something(self: @TContractState, value: u32) -> u32;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod LocalVariablesExample {\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" #[abi(embed_v0)]\n" +" impl LocalVariablesExample of super::" +"ILocalVariablesExample {\n" +" fn do_something(self: @ContractState, value: u32) -> u32 {\n" +" // This variable is local to the current block. It can't be " +"accessed once it goes out of scope.\n" +" let increment = 10;\n" +"\n" +" {\n" +" // The scope of a code block allows for local variable " +"declaration\n" " // We can access variables defined in higher scopes.\n" +" let sum = value + increment;\n" +" sum\n" +" }\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait ILocalVariablesExample {\n" +" fn do_something(self: @TContractState, value: u32) -> u32;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod LocalVariablesExample {\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" #[abi(embed_v0)]\n" +" impl LocalVariablesExample of super::" +"ILocalVariablesExample {\n" +" fn do_something(self: @ContractState, value: u32) -> u32 {\n" +" // This variable is local to the current block. It can't be " +"accessed once it goes out of scope.\n" +" let increment = 10;\n" +"\n" +" {\n" +" // The scope of a code block allows for local variable " +"declaration\n" +" // We can access variables defined in higher scopes.\n" +" let sum = value + increment;\n" +" sum\n" +" }\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch00/basics/variables.md:50 msgid "" @@ -465,23 +610,22 @@ msgstr "" "中尝试它。" #: src/ch00/basics/variables.md:52 -#, fuzzy -msgid "Storage Variables" +msgid "## Storage Variables" msgstr "## 存储用变量" #: src/ch00/basics/variables.md:54 msgid "" "Storage variables are persistent data stored on the blockchain. They can be " -"accessed from one execution to another, allowing the contract to remember " -"and update information over time." +"accessed from one execution to another, allowing the contract to remember and " +"update information over time." msgstr "" -"存储变量是存储在区块链上的持久数据。它们可以在不同的执行过程中被访问,从而使" -"合约能够保存和更新信息。" +"存储变量是存储在区块链上的持久数据。它们可以在不同的执行过程中被访问,从而使合" +"约能够保存和更新信息。" #: src/ch00/basics/variables.md:56 msgid "" -"To write or update a storage variable, you need to interact with the " -"contract through an external entrypoint by sending a transaction." +"To write or update a storage variable, you need to interact with the contract " +"through an external entrypoint by sending a transaction." msgstr "要写入或更新存储变量,需要通过外部入口点发送交易与合约交互。" #: src/ch00/basics/variables.md:58 @@ -494,25 +638,73 @@ msgstr "另一方面,只需与节点交互,就可以免费读取状态变量 msgid "Here's a simple example of a contract with one storage variable:" msgstr "下面是一个带有一个存储变量的简单合约示例:" -#: src/ch00/basics/variables.md:70 +#: src/ch00/basics/variables.md:62 msgid "" -"// All storage variables are contained in a struct called Storage\n" +"```rust\n" +"#[starknet::interface]\n" +"trait IStorageVariableExample {\n" +" fn set(ref self: TContractState, value: u32);\n" +" fn get(self: @TContractState) -> u32;\n" +"}\n" +"#[starknet::contract]\n" +"mod StorageVariablesExample {\n" +" // All storage variables are contained in a struct called Storage\n" " // annotated with the `#[storage]` attribute\n" -msgstr "" - -#: src/ch00/basics/variables.md:74 -msgid "// Storage variable holding a number\n" -msgstr "" - -#: src/ch00/basics/variables.md:80 -msgid "" -"// Write to storage variables by sending a transaction that calls an " +" #[storage]\n" +" struct Storage {\n" +" // Storage variable holding a number\n" +" value: u32\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl StorageVariablesExample of super::" +"IStorageVariableExample {\n" +" // Write to storage variables by sending a transaction that calls an " "external function\n" +" fn set(ref self: ContractState, value: u32) {\n" +" self.value.write(value);\n" +" }\n" +"\n" +" // Read from storage variables without sending transactions\n" +" fn get(self: @ContractState) -> u32 {\n" +" self.value.read()\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" - -#: src/ch00/basics/variables.md:85 -msgid "// Read from storage variables without sending transactions\n" -msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait IStorageVariableExample {\n" +" fn set(ref self: TContractState, value: u32);\n" +" fn get(self: @TContractState) -> u32;\n" +"}\n" +"#[starknet::contract]\n" +"mod StorageVariablesExample {\n" +" // All storage variables are contained in a struct called Storage\n" +" // annotated with the `#[storage]` attribute\n" +" #[storage]\n" +" struct Storage {\n" +" // Storage variable holding a number\n" +" value: u32\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl StorageVariablesExample of super::" +"IStorageVariableExample {\n" +" // Write to storage variables by sending a transaction that calls an " +"external function\n" +" fn set(ref self: ContractState, value: u32) {\n" +" self.value.write(value);\n" +" }\n" +"\n" +" // Read from storage variables without sending transactions\n" +" fn get(self: @ContractState) -> u32 {\n" +" self.value.read()\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch00/basics/variables.md:92 msgid "" @@ -531,18 +723,17 @@ msgstr "" "cairo) 中尝试它。" #: src/ch00/basics/variables.md:94 -#, fuzzy -msgid "Global Variables" +msgid "## Global Variables" msgstr "## 全局变量" #: src/ch00/basics/variables.md:96 msgid "" "Global variables are predefined variables that provide information about the " -"blockchain and the current execution environment. They can be accessed at " -"any time and from anywhere!" +"blockchain and the current execution environment. They can be accessed at any " +"time and from anywhere!" msgstr "" -"全局变量是预定义变量,可提供有关区块链和当前执行环境的信息。可以随时随地访问" -"它们!" +"全局变量是预定义变量,可提供有关区块链和当前执行环境的信息。可以随时随地访问它" +"们!" #: src/ch00/basics/variables.md:98 msgid "" @@ -557,20 +748,60 @@ msgid "" "caller of the current transaction, and the `get_contract_address` function " "returns the address of the current contract." msgstr "" -"例如,`get_caller_address`函数返回当前事务的调用者地址," -"`get_contract_address`函数返回当前合同的地址。" - -#: src/ch00/basics/variables.md:109 -msgid "// import the required functions from the starknet core library\n" -msgstr "" - -#: src/ch00/basics/variables.md:118 -msgid "// Call the get_caller_address function to get the sender address\n" -msgstr "" +"例如,`get_caller_address`函数返回当前事务的调用者地址,`get_contract_address`" +"函数返回当前合同的地址。" -#: src/ch00/basics/variables.md:120 -msgid "// ...\n" +#: src/ch00/basics/variables.md:102 +msgid "" +"```rust\n" +"#[starknet::interface]\n" +"trait IGlobalExample {\n" +" fn foo(ref self: TContractState);\n" +"}\n" +"#[starknet::contract]\n" +"mod GlobalExample {\n" +" // import the required functions from the starknet core library\n" +" use starknet::get_caller_address;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" #[abi(embed_v0)]\n" +" impl GlobalExampleImpl of super::IGlobalExample {\n" +" fn foo(ref self: ContractState) {\n" +" // Call the get_caller_address function to get the sender " +"address\n" +" let caller = get_caller_address();\n" +" // ...\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait IGlobalExample {\n" +" fn foo(ref self: TContractState);\n" +"}\n" +"#[starknet::contract]\n" +"mod GlobalExample {\n" +" // import the required functions from the starknet core library\n" +" use starknet::get_caller_address;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" #[abi(embed_v0)]\n" +" impl GlobalExampleImpl of super::IGlobalExample {\n" +" fn foo(ref self: ContractState) {\n" +" // Call the get_caller_address function to get the sender " +"address\n" +" let caller = get_caller_address();\n" +" // ...\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch00/basics/variables.md:125 msgid "" @@ -578,19 +809,21 @@ msgid "" "contract/0x05bD2F3943bd4e030f85678b55b2EC2C1be939e32388530FB20ED967B3Be433F) " "or play with it in [Remix](https://remix.ethereum.org/?" "#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/" -"blob/main/listings/ch00-getting-started/variables/src/global_variables." -"cairo)." +"blob/main/listings/ch00-getting-started/variables/src/global_variables.cairo)." msgstr "" "访问 [Voyager](https://goerli.voyager.online/" "contract/0x05bD2F3943bd4e030f85678b55b2EC2C1be939e32388530FB20ED967B3Be433F) " "上的合约,或在 [Remix](https://remix.ethereum.org/?" "#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/" -"blob/main/listings/ch00-getting-started/variables/src/global_variables." -"cairo) 中尝试它。" +"blob/main/listings/ch00-getting-started/variables/src/global_variables.cairo) " +"中尝试它。" + +#: src/ch00/basics/visibility-mutability.md:1 +msgid "# Visibility and Mutability" +msgstr "# 可见性和可变性" #: src/ch00/basics/visibility-mutability.md:3 -#, fuzzy -msgid "Visibility" +msgid "## Visibility" msgstr "## 可见性" #: src/ch00/basics/visibility-mutability.md:5 @@ -598,13 +831,9 @@ msgid "There are two types of functions in Starknet contracts:" msgstr "Starknet合约有两种功能:" #: src/ch00/basics/visibility-mutability.md:7 -msgid "Functions that are accessible externally and can be called by anyone." -msgstr "" - -#: src/ch00/basics/visibility-mutability.md:8 -#, fuzzy msgid "" -"Functions that are only accessible internally and can only be called by " +"- Functions that are accessible externally and can be called by anyone.\n" +"- Functions that are only accessible internally and can only be called by " "other functions in the contract." msgstr "" "- 外部可访问、任何人都可调用的函数。\n" @@ -612,53 +841,42 @@ msgstr "" #: src/ch00/basics/visibility-mutability.md:10 msgid "" -"These functions are also typically divided into two different " -"implementations blocks. The first `impl` block for externally accessible " -"functions is explicitly annotated with an `#[abi(embed_v0)]` attribute. This " -"indicates that all the functions inside this block can be called either as a " +"These functions are also typically divided into two different implementations " +"blocks. The first `impl` block for externally accessible functions is " +"explicitly annotated with an `#[abi(embed_v0)]` attribute. This indicates " +"that all the functions inside this block can be called either as a " "transaction or as a view function. The second `impl` block for internally " "accessible functions is not annotated with any attribute, which means that " "all the functions inside this block are private by default." msgstr "" -"这些函数通常也分为两个不同的实现块。第一个`impl`块用于外部访问的函数,明确标" -"注了 `#[abi(embed_v0)]`属性。这表明该代码块中的所有函数都可以作为交易或视图函" -"数调用。第二个用于内部可访问函数的 `impl` 块没有注释任何属性,这意味着该块中" -"的所有函数默认都是私有的。" +"这些函数通常也分为两个不同的实现块。第一个`impl`块用于外部访问的函数,明确标注" +"了 `#[abi(embed_v0)]`属性。这表明该代码块中的所有函数都可以作为交易或视图函数" +"调用。第二个用于内部可访问函数的 `impl` 块没有注释任何属性,这意味着该块中的所" +"有函数默认都是私有的。" #: src/ch00/basics/visibility-mutability.md:12 -#, fuzzy -msgid "State Mutability" +msgid "## State Mutability" msgstr "## 状态可变性" #: src/ch00/basics/visibility-mutability.md:14 -#, fuzzy msgid "" "Regardless of whether a function is internal or external, it can either " "modify the contract's state or not. When we declare functions that interact " -"with storage variables inside a smart contract, we need to explicitly state " -"that we are accessing the `ContractState` by adding it as the first " -"parameter of the function. This can be done in two different ways:" +"with storage variables inside a smart contract,\n" +"we need to explicitly state that we are accessing the `ContractState` by " +"adding it as the first parameter of the function. This can be done in two " +"different ways:" msgstr "" -"无论函数是内部函数还是外部函数,它都可以修改或不修改合约的状态。当我们在智能" -"合约中声明与存储变量交互的函数时,\n" +"无论函数是内部函数还是外部函数,它都可以修改或不修改合约的状态。当我们在智能合" +"约中声明与存储变量交互的函数时,\n" "我们需要将 `ContractState`添加为函数的第一个参数,明确说明我们正在访问 合约的" "状态。这有两种不同的方法:" #: src/ch00/basics/visibility-mutability.md:17 -#, fuzzy -msgid "" -"If we want our function to be able to mutate the state of the contract, we " -"pass it by reference like this: `ref self: ContractState`." -msgstr "" -"- 如果我们希望我们的函数能够更改合约的状态,我们可以像这样通过引用来传递它:" -"ref self:ContractState`。\n" -"- 如果我们希望我们的函数是只读的,并且不更改合约的状态,我们可以通过快照传递" -"它,如下所示:`self:@ContractState`." - -#: src/ch00/basics/visibility-mutability.md:18 -#, fuzzy msgid "" -"If we want our function to be read-only and not mutate the state of the " +"- If we want our function to be able to mutate the state of the contract, we " +"pass it by reference like this: `ref self: ContractState`.\n" +"- If we want our function to be read-only and not mutate the state of the " "contract, we pass it by snapshot like this: `self: @ContractState`." msgstr "" "- 如果我们希望我们的函数能够更改合约的状态,我们可以像这样通过引用来传递它:" @@ -667,13 +885,12 @@ msgstr "" "它,如下所示:`self:@ContractState`." #: src/ch00/basics/visibility-mutability.md:20 -#, fuzzy msgid "" "Read-only functions, also called view functions, can be directly called " "without making a transaction. You can interact with them directly through a " -"RPC node to read the contract's state, and they're free to call! External " -"functions, that modify the contract's state, on the other side can only be " -"called by making a transaction." +"RPC node to read the contract's state, and they're free to call!\n" +"External functions, that modify the contract's state, on the other side can " +"only be called by making a transaction." msgstr "" "只读函数(也称为视图函数)可以直接调用,无需进行事务处理。你可以直接通过 RPC " "节点与它们交互,读取合约的状态,而且可以自由调用!\n" @@ -681,59 +898,131 @@ msgstr "" #: src/ch00/basics/visibility-mutability.md:23 msgid "" -"Internal functions can't be called externally, but the same principle " -"applies regarding state mutability." +"Internal functions can't be called externally, but the same principle applies " +"regarding state mutability." msgstr "内部函数不能被外部调用,同样的原则也适用于状态可变性。" #: src/ch00/basics/visibility-mutability.md:25 msgid "Let's take a look at a simple example contract to see these in action:" msgstr "让我们通过一个简单的合约示例来了解这些功能:" -#: src/ch00/basics/visibility-mutability.md:42 +#: src/ch00/basics/visibility-mutability.md:27 msgid "" -"// The `abi(embed_v0)` attribute indicates that all the functions in this " +"```rust\n" +"#[starknet::interface]\n" +"trait IExampleContract {\n" +" fn set(ref self: TContractState, value: u32);\n" +" fn get(self: @TContractState) -> u32;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod ExampleContract {\n" +" #[storage]\n" +" struct Storage {\n" +" value: u32\n" +" }\n" +"\n" +"\n" +" // The `abi(embed_v0)` attribute indicates that all the functions in this " "implementation can be called externally.\n" " // Omitting this attribute would make all the functions in this " "implementation internal.\n" -msgstr "" - -#: src/ch00/basics/visibility-mutability.md:46 -msgid "" -"// The `set` function can be called externally because it is written inside " -"an implementation marked as `#[external]`.\n" +" #[abi(embed_v0)]\n" +" impl ExampleContract of super::IExampleContract {\n" +" // The `set` function can be called externally because it is written " +"inside an implementation marked as `#[external]`.\n" " // It can modify the contract's state as it is passed as a " "reference.\n" -msgstr "" - -#: src/ch00/basics/visibility-mutability.md:52 -msgid "" -"// The `get` function can be called externally because it is written inside " -"an implementation marked as `#[external]`.\n" +" fn set(ref self: ContractState, value: u32) {\n" +" self.value.write(value);\n" +" }\n" +"\n" +" // The `get` function can be called externally because it is written " +"inside an implementation marked as `#[external]`.\n" " // However, it can't modify the contract's state is passed as a " "snapshot: it is only a \"view\" function.\n" -msgstr "" - -#: src/ch00/basics/visibility-mutability.md:55 -msgid "" -"// We can call an internal function from any functions within the contract\n" -msgstr "" - -#: src/ch00/basics/visibility-mutability.md:60 -msgid "" -"// The lack of the `external` attribute indicates that all the functions in " -"this implementation can only be called internally.\n" +" fn get(self: @ContractState) -> u32 {\n" +" // We can call an internal function from any functions within the " +"contract\n" +" PrivateFunctionsTrait::_read_value(self)\n" +" }\n" +" }\n" +"\n" +" // The lack of the `external` attribute indicates that all the functions " +"in this implementation can only be called internally.\n" " // We name the trait `PrivateFunctionsTrait` to indicate that it is an " "internal trait allowing us to call internal functions.\n" -msgstr "" - -#: src/ch00/basics/visibility-mutability.md:64 -msgid "" -"// The `_read_value` function is outside the implementation that is marked " -"as `#[abi(embed_v0)]`, so it's an _internal_ function\n" +" #[generate_trait]\n" +" impl PrivateFunctions of PrivateFunctionsTrait {\n" +" // The `_read_value` function is outside the implementation that is " +"marked as `#[abi(embed_v0)]`, so it's an _internal_ function\n" " // and can only be called from within the contract.\n" " // However, it can't modify the contract's state is passed as a " "snapshot: it is only a \"view\" function.\n" +" fn _read_value(self: @ContractState) -> u32 {\n" +" self.value.read()\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait IExampleContract {\n" +" fn set(ref self: TContractState, value: u32);\n" +" fn get(self: @TContractState) -> u32;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod ExampleContract {\n" +" #[storage]\n" +" struct Storage {\n" +" value: u32\n" +" }\n" +"\n" +"\n" +" // The `abi(embed_v0)` attribute indicates that all the functions in this " +"implementation can be called externally.\n" +" // Omitting this attribute would make all the functions in this " +"implementation internal.\n" +" #[abi(embed_v0)]\n" +" impl ExampleContract of super::IExampleContract {\n" +" // The `set` function can be called externally because it is written " +"inside an implementation marked as `#[external]`.\n" +" // It can modify the contract's state as it is passed as a " +"reference.\n" +" fn set(ref self: ContractState, value: u32) {\n" +" self.value.write(value);\n" +" }\n" +"\n" +" // The `get` function can be called externally because it is written " +"inside an implementation marked as `#[external]`.\n" +" // However, it can't modify the contract's state is passed as a " +"snapshot: it is only a \"view\" function.\n" +" fn get(self: @ContractState) -> u32 {\n" +" // We can call an internal function from any functions within the " +"contract\n" +" PrivateFunctionsTrait::_read_value(self)\n" +" }\n" +" }\n" +"\n" +" // The lack of the `external` attribute indicates that all the functions " +"in this implementation can only be called internally.\n" +" // We name the trait `PrivateFunctionsTrait` to indicate that it is an " +"internal trait allowing us to call internal functions.\n" +" #[generate_trait]\n" +" impl PrivateFunctions of PrivateFunctionsTrait {\n" +" // The `_read_value` function is outside the implementation that is " +"marked as `#[abi(embed_v0)]`, so it's an _internal_ function\n" +" // and can only be called from within the contract.\n" +" // However, it can't modify the contract's state is passed as a " +"snapshot: it is only a \"view\" function.\n" +" fn _read_value(self: @ContractState) -> u32 {\n" +" self.value.read()\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch00/basics/visibility-mutability.md:73 msgid "" @@ -750,9 +1039,13 @@ msgstr "" "blob/main/listings/ch00-getting-started/visibility/src/visibility.cairo) 中尝" "试它。" +#: src/ch00/basics/visibility-mutability.md:75 +#: src/ch00/interacting/interacting.md:4 +msgid "
Last change: 2023-10-19
" +msgstr "
Last change: 2023-10-19
" + #: src/ch00/basics/counter.md:1 -#, fuzzy -msgid "Simple Counter" +msgid "# Simple Counter" msgstr "# 简单计数器" #: src/ch00/basics/counter.md:3 @@ -765,35 +1058,104 @@ msgstr "这个合约是这样工作的:" #: src/ch00/basics/counter.md:7 msgid "" -"The contract has a state variable called 'counter' that is initialized to 0." -msgstr "" - -#: src/ch00/basics/counter.md:9 -msgid "" -"When a user calls 'increment', the contract increments the counter by 1." +"- The contract has a state variable called 'counter' that is initialized to " +"0.\n" +"\n" +"- When a user calls 'increment', the contract increments the counter by 1.\n" +"\n" +"- When a user calls 'decrement', the contract decrements the counter by 1." msgstr "" +"- 合约有一个名为 'counter'的状态变量,初始化为 0。\n" +"\n" +"- 当用户调用 'increment'时,合约会将计数器递增 1。\n" +"\n" +"- 当用户调用 'decrement'时,合约会将计数器递减 1。" -#: src/ch00/basics/counter.md:11 +#: src/ch00/basics/counter.md:13 msgid "" -"When a user calls 'decrement', the contract decrements the counter by 1." -msgstr "" - -#: src/ch00/basics/counter.md:25 -#, fuzzy -msgid "// Counter variable\n" -msgstr "计数器示例" - -#: src/ch00/basics/counter.md:31 -msgid "// Store initial value\n" -msgstr "" - -#: src/ch00/basics/counter.md:42 -msgid "// Store counter value + 1\n" -msgstr "" - -#: src/ch00/basics/counter.md:47 -msgid "// Store counter value - 1\n" +"```rust\n" +"#[starknet::interface]\n" +"trait ISimpleCounter {\n" +" fn get_current_count(self: @TContractState) -> u128;\n" +" fn increment(ref self: TContractState);\n" +" fn decrement(ref self: TContractState);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod SimpleCounter {\n" +" #[storage]\n" +" struct Storage {\n" +" // Counter variable\n" +" counter: u128,\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(ref self: ContractState, init_value: u128) {\n" +" // Store initial value\n" +" self.counter.write(init_value);\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl SimpleCounter of super::ISimpleCounter {\n" +" fn get_current_count(self: @ContractState) -> u128 {\n" +" return self.counter.read();\n" +" }\n" +"\n" +" fn increment(ref self: ContractState) {\n" +" // Store counter value + 1\n" +" let counter = self.counter.read() + 1;\n" +" self.counter.write(counter);\n" +" }\n" +" fn decrement(ref self: ContractState) {\n" +" // Store counter value - 1\n" +" let counter = self.counter.read() - 1;\n" +" self.counter.write(counter);\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait ISimpleCounter {\n" +" fn get_current_count(self: @TContractState) -> u128;\n" +" fn increment(ref self: TContractState);\n" +" fn decrement(ref self: TContractState);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod SimpleCounter {\n" +" #[storage]\n" +" struct Storage {\n" +" // Counter variable\n" +" counter: u128,\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(ref self: ContractState, init_value: u128) {\n" +" // Store initial value\n" +" self.counter.write(init_value);\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl SimpleCounter of super::ISimpleCounter {\n" +" fn get_current_count(self: @ContractState) -> u128 {\n" +" return self.counter.read();\n" +" }\n" +"\n" +" fn increment(ref self: ContractState) {\n" +" // Store counter value + 1\n" +" let counter = self.counter.read() + 1;\n" +" self.counter.write(counter);\n" +" }\n" +" fn decrement(ref self: ContractState) {\n" +" // Store counter value - 1\n" +" let counter = self.counter.read() - 1;\n" +" self.counter.write(counter);\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch00/basics/counter.md:54 msgid "" @@ -810,6 +1172,14 @@ msgstr "" "blob/main/listings/ch00-getting-started/counter/src/contracts.cairo) 中尝试" "它。" +#: src/ch00/basics/counter.md:56 +msgid "
Last change: 2023-11-04
" +msgstr "
Last change: 2023-11-04
" + +#: src/ch00/basics/mappings.md:1 +msgid "# Mappings" +msgstr "# 映射" + #: src/ch00/basics/mappings.md:3 msgid "" "Maps are a key-value data structure used to store data within a smart " @@ -822,14 +1192,13 @@ msgstr "" "中使用,不能用在其他地方。" #: src/ch00/basics/mappings.md:5 -#, fuzzy msgid "" "Here we demonstrate how to use the `LegacyMap` type within a Cairo contract, " "to map between a key of type `ContractAddress` and value of type `felt252`. " -"The key-value types are specified within angular brackets \\<\\>. We write " -"to the map by calling the `write()` method, passing in both the key and " -"value. Similarly, we can read the value associated with a given key by " -"calling the `read()` method and passing in the relevant key." +"The key-value types are specified within angular brackets <>. We write to the " +"map by calling the `write()` method, passing in both the key and value. " +"Similarly, we can read the value associated with a given key by calling the " +"`read()` method and passing in the relevant key." msgstr "" "在此,我们演示如何在Cairo合约中使用 `LegacyMap` 类型,在 `ContractAddress` 类" "型的键和 `felt252` 类型的值之间进行映射。键值类型在角括号 <> 中指定。我们通过" @@ -842,34 +1211,95 @@ msgstr "一些补充说明:" #: src/ch00/basics/mappings.md:9 msgid "" -"More complex key-value mappings are possible, for example we could use " +"- More complex key-value mappings are possible, for example we could use " "`LegacyMap::<(ContractAddress, ContractAddress), felt252>` to create an " -"allowance on an ERC20 token contract." -msgstr "" - -#: src/ch00/basics/mappings.md:11 -#, fuzzy -msgid "" -"In mappings, the address of the value at key `k_1,...,k_n` is `h(..." +"allowance on an ERC20 token contract.\n" +"\n" +"- In mappings, the address of the value at key `k_1,...,k_n` is `h(..." "h(h(sn_keccak(variable_name),k_1),k_2),...,k_n)` where `ℎ` is the Pedersen " -"hash and the final value is taken `mod2251−256`. You can learn more about " -"the contract storage layout in the [Starknet Documentation](https://docs." -"starknet.io/documentation/architecture_and_concepts/Contracts/contract-" -"storage/#storage_variables)" +"hash and the final value is taken `mod2251−256`. You can learn more about the " +"contract storage layout in the [Starknet Documentation](https://docs.starknet." +"io/documentation/architecture_and_concepts/Contracts/contract-storage/" +"#storage_variables)" msgstr "" "- 也有更复杂的键值对映射,例如,我们可以使用 `LegacyMap::<(ContractAddress, " "ContractAddress), felt252>` 在 ERC20 代币合约上创建一个代币授权许可。\n" "\n" "- 在映射中,键`k_1,...,k_n`处的值的地址是`h(...h(h(sn_keccak(variable_name)," "k_1),k_2),...,k_n)`,其中 `ℎ` 是 Pedersen 哈希值,最终值取`mod2251-256`。有关" -"合约存储布局的更多信息,请参阅 [Starknet Documentation](https://docs." -"starknet.io/documentation/architecture_and_concepts/Contracts/contract-" -"storage/#storage_variables)。" +"合约存储布局的更多信息,请参阅 [Starknet Documentation](https://docs.starknet." +"io/documentation/architecture_and_concepts/Contracts/contract-storage/" +"#storage_variables)。" -#: src/ch00/basics/mappings.md:28 +#: src/ch00/basics/mappings.md:13 msgid "" -"// The `LegacyMap` type is only available inside the `Storage` struct.\n" +"```rust\n" +"use starknet::ContractAddress;\n" +"\n" +"#[starknet::interface]\n" +"trait IMapContract {\n" +" fn set(ref self: TContractState, key: ContractAddress, value: felt252);\n" +" fn get(self: @TContractState, key: ContractAddress) -> felt252;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod MapContract {\n" +" use starknet::ContractAddress;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" // The `LegacyMap` type is only available inside the `Storage` " +"struct.\n" +" map: LegacyMap::,\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl MapContractImpl of super::IMapContract {\n" +" fn set(ref self: ContractState, key: ContractAddress, value: felt252) " +"{\n" +" self.map.write(key, value);\n" +" }\n" +"\n" +" fn get(self: @ContractState, key: ContractAddress) -> felt252 {\n" +" self.map.read(key)\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"use starknet::ContractAddress;\n" +"\n" +"#[starknet::interface]\n" +"trait IMapContract {\n" +" fn set(ref self: TContractState, key: ContractAddress, value: felt252);\n" +" fn get(self: @TContractState, key: ContractAddress) -> felt252;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod MapContract {\n" +" use starknet::ContractAddress;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" // The `LegacyMap` type is only available inside the `Storage` " +"struct.\n" +" map: LegacyMap::,\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl MapContractImpl of super::IMapContract {\n" +" fn set(ref self: ContractState, key: ContractAddress, value: felt252) " +"{\n" +" self.map.write(key, value);\n" +" }\n" +"\n" +" fn get(self: @ContractState, key: ContractAddress) -> felt252 {\n" +" self.map.read(key)\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch00/basics/mappings.md:44 msgid "" @@ -886,17 +1316,20 @@ msgstr "" "blob/main/listings/ch00-getting-started/mappings/src/mappings.cairo) 中尝试" "它。" +#: src/ch00/basics/errors.md:1 +msgid "# Errors" +msgstr "# 错误" + #: src/ch00/basics/errors.md:3 -#, fuzzy msgid "" "Errors can be used to handle validation and other conditions that may occur " -"during the execution of a smart contract. If an error is thrown during the " -"execution of a smart contract call, the execution is stopped and any changes " -"made during the transaction are reverted." +"during the execution of a smart contract.\n" +"If an error is thrown during the execution of a smart contract call, the " +"execution is stopped and any changes made during the transaction are reverted." msgstr "" "错误可用于处理智能合约执行过程中可能发生的验证和其他条件。\n" -"如果在执行智能合约调用期间抛出错误,则将停止执行,并恢复在交易期间所做的任何" -"更改。" +"如果在执行智能合约调用期间抛出错误,则将停止执行,并恢复在交易期间所做的任何更" +"改。" #: src/ch00/basics/errors.md:6 msgid "To throw an error, use the `assert` or `panic` functions:" @@ -904,18 +1337,16 @@ msgstr "要抛出错误,请使用 `assert` 或 `panic`函数:" #: src/ch00/basics/errors.md:8 msgid "" -"`assert` is used to validate conditions. If the check fails, an error is " -"thrown along with a specified value, often a message. It's similar to the " -"`require` statement in Solidity." -msgstr "" - -#: src/ch00/basics/errors.md:12 -#, fuzzy -msgid "" -"`panic` immediately halt the execution with the given error value. It should " -"be used when the condition to check is complex and for internal errors. It's " -"similar to the `revert` statement in Solidity. (Use `panic_with_felt252` to " -"be able to directly pass a felt252 as the error value)" +"- `assert` is used to validate conditions.\n" +" If the check fails, an error is thrown along with a specified value, often " +"a message.\n" +" It's similar to the `require` statement in Solidity.\n" +"\n" +"- `panic` immediately halt the execution with the given error value.\n" +" It should be used when the condition to check is complex and for internal " +"errors. It's similar to the `revert` statement in Solidity.\n" +" (Use `panic_with_felt252` to be able to directly pass a felt252 as the " +"error value)" msgstr "" "- 'assert' 用于验证条件。\n" " 如果检查失败,则会引发错误以及指定的值,通常是一条消息。\n" @@ -930,17 +1361,67 @@ msgstr "" msgid "Here's a simple example that demonstrates the use of these functions:" msgstr "下面是一个简单的示例,演示了这些函数的用法:" -#: src/ch00/basics/errors.md:32 +#: src/ch00/basics/errors.md:18 msgid "" -"// Assert used to validate a condition\n" +"```rust\n" +"#[starknet::interface]\n" +"trait IErrorsExample {\n" +" fn test_assert(self: @TContractState, i: u256);\n" +" fn test_panic(self: @TContractState, i: u256);\n" +"}\n" +"#[starknet::contract]\n" +"mod ErrorsExample {\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ErrorsExample of super::IErrorsExample {\n" +" fn test_assert(self: @ContractState, i: u256) {\n" +" // Assert used to validate a condition\n" " // and abort execution if the condition is not met\n" +" assert(i > 0, 'i must be greater than 0');\n" +" }\n" +"\n" +" fn test_panic(self: @ContractState, i: u256) {\n" +" if (i == 0) {\n" +" // Panic used to abort execution directly\n" +" panic_with_felt252('i must not be 0');\n" +" }\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" - -#: src/ch00/basics/errors.md:39 -msgid "// Panic used to abort execution directly\n" -msgstr "" - -#: src/ch00/basics/errors.md:46 +"```rust\n" +"#[starknet::interface]\n" +"trait IErrorsExample {\n" +" fn test_assert(self: @TContractState, i: u256);\n" +" fn test_panic(self: @TContractState, i: u256);\n" +"}\n" +"#[starknet::contract]\n" +"mod ErrorsExample {\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ErrorsExample of super::IErrorsExample {\n" +" fn test_assert(self: @ContractState, i: u256) {\n" +" // Assert used to validate a condition\n" +" // and abort execution if the condition is not met\n" +" assert(i > 0, 'i must be greater than 0');\n" +" }\n" +"\n" +" fn test_panic(self: @ContractState, i: u256) {\n" +" if (i == 0) {\n" +" // Panic used to abort execution directly\n" +" panic_with_felt252('i must not be 0');\n" +" }\n" +" }\n" +" }\n" +"}\n" +"```" + +#: src/ch00/basics/errors.md:46 msgid "" "Visit contract on [Voyager](https://goerli.voyager.online/" "contract/0x0022664463FF0b711CC9B549a9E87d65A0882bB1D29338C4108696B8F2216a40) " @@ -952,20 +1433,89 @@ msgstr "" "contract/0x0022664463FF0b711CC9B549a9E87d65A0882bB1D29338C4108696B8F2216a40) " "上访问合约或在 [Remix](https://remix.ethereum.org/?" "#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/" -"blob/main/listings/ch00-getting-started/errors/src/simple_errors.cairo) 中尝" -"试它。" +"blob/main/listings/ch00-getting-started/errors/src/simple_errors.cairo) 中尝试" +"它。" #: src/ch00/basics/errors.md:48 -#, fuzzy -msgid "Custom errors" +msgid "## Custom errors" msgstr "## 自定义错误" #: src/ch00/basics/errors.md:50 msgid "" -"You can make error handling easier by defining your error codes in a " -"specific module." +"You can make error handling easier by defining your error codes in a specific " +"module." msgstr "您可以通过在特定模块中定义错误代码来简化错误处理。" +#: src/ch00/basics/errors.md:52 +msgid "" +"```rust\n" +"mod Errors {\n" +" const NOT_POSITIVE: felt252 = 'must be greater than 0';\n" +" const NOT_NULL: felt252 = 'must not be null';\n" +"}\n" +"\n" +"#[starknet::interface]\n" +"trait ICustomErrorsExample {\n" +" fn test_assert(self: @TContractState, i: u256);\n" +" fn test_panic(self: @TContractState, i: u256);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod CustomErrorsExample {\n" +" use super::Errors;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" #[abi(embed_v0)]\n" +" impl CustomErrorsExample of super::ICustomErrorsExample {\n" +" fn test_assert(self: @ContractState, i: u256) {\n" +" assert(i > 0, Errors::NOT_POSITIVE);\n" +" }\n" +"\n" +" fn test_panic(self: @ContractState, i: u256) {\n" +" if (i == 0) {\n" +" panic_with_felt252(Errors::NOT_NULL);\n" +" }\n" +" }\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"mod Errors {\n" +" const NOT_POSITIVE: felt252 = 'must be greater than 0';\n" +" const NOT_NULL: felt252 = 'must not be null';\n" +"}\n" +"\n" +"#[starknet::interface]\n" +"trait ICustomErrorsExample {\n" +" fn test_assert(self: @TContractState, i: u256);\n" +" fn test_panic(self: @TContractState, i: u256);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod CustomErrorsExample {\n" +" use super::Errors;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" #[abi(embed_v0)]\n" +" impl CustomErrorsExample of super::ICustomErrorsExample {\n" +" fn test_assert(self: @ContractState, i: u256) {\n" +" assert(i > 0, Errors::NOT_POSITIVE);\n" +" }\n" +"\n" +" fn test_panic(self: @ContractState, i: u256) {\n" +" if (i == 0) {\n" +" panic_with_felt252(Errors::NOT_NULL);\n" +" }\n" +" }\n" +" }\n" +"}\n" +"```" + #: src/ch00/basics/errors.md:85 msgid "" "Visit contract on [Voyager](https://goerli.voyager.online/" @@ -978,12 +1528,11 @@ msgstr "" "contract/0x0501CD5da5B453a18515B5A20b8029bd7583DFE7a399ad9f79c284F7829e4A57) " "上访问 contract 或在 [Remix](https://remix.ethereum.org/?" "#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/" -"blob/main/listings/ch00-getting-started/errors/src/custom_errors.cairo) 中尝" -"试它。" +"blob/main/listings/ch00-getting-started/errors/src/custom_errors.cairo) 中尝试" +"它。" #: src/ch00/basics/errors.md:87 -#, fuzzy -msgid "Vault example" +msgid "## Vault example" msgstr "## Vault 示例" #: src/ch00/basics/errors.md:89 @@ -992,13 +1541,101 @@ msgid "" "contract:" msgstr "下面是另一个示例,演示了在更复杂的合约中使用错误:" -#: src/ch00/basics/errors.md:93 -msgid "// you can define more errors here\n" -msgstr "" - -#: src/ch00/basics/errors.md:125 -msgid "// Or using panic:\n" +#: src/ch00/basics/errors.md:91 +msgid "" +"```rust\n" +"mod VaultErrors {\n" +" const INSUFFICIENT_BALANCE: felt252 = 'insufficient_balance';\n" +"// you can define more errors here\n" +"}\n" +"\n" +"#[starknet::interface]\n" +"trait IVaultErrorsExample {\n" +" fn deposit(ref self: TContractState, amount: u256);\n" +" fn withdraw(ref self: TContractState, amount: u256);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod VaultErrorsExample {\n" +" use super::VaultErrors;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" balance: u256,\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl VaultErrorsExample of super::IVaultErrorsExample {\n" +" fn deposit(ref self: ContractState, amount: u256) {\n" +" let mut balance = self.balance.read();\n" +" balance = balance + amount;\n" +" self.balance.write(balance);\n" +" }\n" +"\n" +" fn withdraw(ref self: ContractState, amount: u256) {\n" +" let mut balance = self.balance.read();\n" +"\n" +" assert(balance >= amount, VaultErrors::INSUFFICIENT_BALANCE);\n" +"\n" +" // Or using panic:\n" +" if (balance >= amount) {\n" +" panic_with_felt252(VaultErrors::INSUFFICIENT_BALANCE);\n" +" }\n" +"\n" +" let balance = balance - amount;\n" +"\n" +" self.balance.write(balance);\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"mod VaultErrors {\n" +" const INSUFFICIENT_BALANCE: felt252 = 'insufficient_balance';\n" +"// you can define more errors here\n" +"}\n" +"\n" +"#[starknet::interface]\n" +"trait IVaultErrorsExample {\n" +" fn deposit(ref self: TContractState, amount: u256);\n" +" fn withdraw(ref self: TContractState, amount: u256);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod VaultErrorsExample {\n" +" use super::VaultErrors;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" balance: u256,\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl VaultErrorsExample of super::IVaultErrorsExample {\n" +" fn deposit(ref self: ContractState, amount: u256) {\n" +" let mut balance = self.balance.read();\n" +" balance = balance + amount;\n" +" self.balance.write(balance);\n" +" }\n" +"\n" +" fn withdraw(ref self: ContractState, amount: u256) {\n" +" let mut balance = self.balance.read();\n" +"\n" +" assert(balance >= amount, VaultErrors::INSUFFICIENT_BALANCE);\n" +"\n" +" // Or using panic:\n" +" if (balance >= amount) {\n" +" panic_with_felt252(VaultErrors::INSUFFICIENT_BALANCE);\n" +" }\n" +"\n" +" let balance = balance - amount;\n" +"\n" +" self.balance.write(balance);\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch00/basics/errors.md:137 msgid "" @@ -1015,22 +1652,25 @@ msgstr "" "blob/main/listings/ch00-getting-started/errors/src/vault_errors.cairo) 中尝试" "它。" +#: src/ch00/basics/events.md:1 +msgid "# Events" +msgstr "# 事件" + #: src/ch00/basics/events.md:3 -#, fuzzy msgid "" "Events are a way to emit data from a contract. All events must be defined in " -"the `Event` enum, which must be annotated with the `#[event]` attribute. An " -"event is defined as struct that derives the `#[starknet::Event]` trait. The " -"fields of that struct correspond to the data that will be emitted. An event " -"can be indexed for easy and fast access when querying the data at a later " -"time. Events data can be indexed by adding a `#[key]` attribute to a field " -"member." +"the `Event` enum, which must be annotated with the `#[event]` attribute.\n" +"An event is defined as struct that derives the `#[starknet::Event]` trait. " +"The fields of that struct correspond to the data that will be emitted. An " +"event can be indexed for easy and fast access when querying the data at a " +"later time. Events data can be indexed by adding a `#[key]` attribute to a " +"field member." msgstr "" "事件是从合约发出数据的一种方式。所有事件都必须在`Event`枚举中定义,该枚举必须" "使用`#[event]`属性进行注释。\n" -"事件被定义为派生`#[starknet::Event]`特征的结构。该结构的字段对应于将要发出的" -"数据。可以对事件编制索引,以便在以后查询数据时轻松快速地访问。可以通过向字段" -"成员添加`#[key]` 属性来索引事件数据。" +"事件被定义为派生`#[starknet::Event]`特征的结构。该结构的字段对应于将要发出的数" +"据。可以对事件编制索引,以便在以后查询数据时轻松快速地访问。可以通过向字段成员" +"添加`#[key]` 属性来索引事件数据。" #: src/ch00/basics/events.md:6 msgid "" @@ -1040,31 +1680,131 @@ msgstr "" "下面是合约使用事件的简单示例,这些事件在每次计数器通过“increment”函数递增时发" "出一个事件:" -#: src/ch00/basics/events.md:18 -#, fuzzy -msgid "// Counter value\n" -msgstr "计数器示例" - -#: src/ch00/basics/events.md:24 +#: src/ch00/basics/events.md:8 msgid "" -"// The event enum must be annotated with the `#[event]` attribute.\n" +"```rust\n" +"#[starknet::interface]\n" +"trait IEventCounter {\n" +" fn increment(ref self: TContractState);\n" +"}\n" +"#[starknet::contract]\n" +"mod EventCounter {\n" +" use starknet::{get_caller_address, ContractAddress};\n" +" #[storage]\n" +" struct Storage {\n" +" // Counter value\n" +" counter: u128,\n" +" }\n" +"\n" +" #[event]\n" +" #[derive(Drop, starknet::Event)]\n" +" // The event enum must be annotated with the `#[event]` attribute.\n" " // It must also derive the `Drop` and `starknet::Event` traits.\n" -msgstr "" - -#: src/ch00/basics/events.md:31 -msgid "" -"// By deriving the `starknet::Event` trait, we indicate to the compiler " +" enum Event {\n" +" CounterIncreased: CounterIncreased,\n" +" UserIncreaseCounter: UserIncreaseCounter\n" +" }\n" +"\n" +" // By deriving the `starknet::Event` trait, we indicate to the compiler " "that\n" " // this struct will be used when emitting events.\n" +" #[derive(Drop, starknet::Event)]\n" +" struct CounterIncreased {\n" +" amount: u128\n" +" }\n" +"\n" +" #[derive(Drop, starknet::Event)]\n" +" struct UserIncreaseCounter {\n" +" // The `#[key]` attribute indicates that this event will be indexed.\n" +" #[key]\n" +" user: ContractAddress,\n" +" new_value: u128,\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl EventCounter of super::IEventCounter {\n" +" fn increment(ref self: ContractState) {\n" +" let mut counter = self.counter.read();\n" +" counter += 1;\n" +" self.counter.write(counter);\n" +" // Emit event\n" +" self.emit(Event::CounterIncreased(CounterIncreased { amount: " +"1 }));\n" +" self\n" +" .emit(\n" +" Event::UserIncreaseCounter(\n" +" UserIncreaseCounter {\n" +" user: get_caller_address(), new_value: self." +"counter.read()\n" +" }\n" +" )\n" +" );\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" - -#: src/ch00/basics/events.md:40 -msgid "// The `#[key]` attribute indicates that this event will be indexed.\n" -msgstr "" - -#: src/ch00/basics/events.md:52 -msgid "// Emit event\n" -msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait IEventCounter {\n" +" fn increment(ref self: TContractState);\n" +"}\n" +"#[starknet::contract]\n" +"mod EventCounter {\n" +" use starknet::{get_caller_address, ContractAddress};\n" +" #[storage]\n" +" struct Storage {\n" +" // Counter value\n" +" counter: u128,\n" +" }\n" +"\n" +" #[event]\n" +" #[derive(Drop, starknet::Event)]\n" +" // The event enum must be annotated with the `#[event]` attribute.\n" +" // It must also derive the `Drop` and `starknet::Event` traits.\n" +" enum Event {\n" +" CounterIncreased: CounterIncreased,\n" +" UserIncreaseCounter: UserIncreaseCounter\n" +" }\n" +"\n" +" // By deriving the `starknet::Event` trait, we indicate to the compiler " +"that\n" +" // this struct will be used when emitting events.\n" +" #[derive(Drop, starknet::Event)]\n" +" struct CounterIncreased {\n" +" amount: u128\n" +" }\n" +"\n" +" #[derive(Drop, starknet::Event)]\n" +" struct UserIncreaseCounter {\n" +" // The `#[key]` attribute indicates that this event will be indexed.\n" +" #[key]\n" +" user: ContractAddress,\n" +" new_value: u128,\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl EventCounter of super::IEventCounter {\n" +" fn increment(ref self: ContractState) {\n" +" let mut counter = self.counter.read();\n" +" counter += 1;\n" +" self.counter.write(counter);\n" +" // Emit event\n" +" self.emit(Event::CounterIncreased(CounterIncreased { amount: " +"1 }));\n" +" self\n" +" .emit(\n" +" Event::UserIncreaseCounter(\n" +" UserIncreaseCounter {\n" +" user: get_caller_address(), new_value: self." +"counter.read()\n" +" }\n" +" )\n" +" );\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch00/basics/events.md:66 msgid "" @@ -1080,25 +1820,89 @@ msgstr "" "#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/" "blob/main/listings/ch00-getting-started/events/src/counter.cairo) 中尝试它。" +#: src/ch00/basics/storing-custom-types.md:1 +msgid "# Storing Custom Types" +msgstr "# 存储自定义类型" + #: src/ch00/basics/storing-custom-types.md:3 msgid "" "While native types can be stored in a contract's storage without any " "additional work, custom types require a bit more work. This is because at " "compile time, the compiler does not know how to store custom types in " -"storage. To solve this, we need to implement the `Store` trait for our " -"custom type. Hopefully, we can just derive this trait for our custom type - " -"unless it contains arrays or dictionaries." +"storage. To solve this, we need to implement the `Store` trait for our custom " +"type. Hopefully, we can just derive this trait for our custom type - unless " +"it contains arrays or dictionaries." msgstr "" -"虽然本机类型可以存储在合约的存储中,而无需任何额外的工作,但自定义类型需要更" -"多的工作。这是因为在编译时,编译器不知道如何在存储中存储自定义类型。为了解决" -"这个问题,我们需要为我们的自定义类型实现 `Store`特征。希望我们可以为我们的自" -"定义类型派生这个特征 - 除非它包含数组或字典。" +"虽然本机类型可以存储在合约的存储中,而无需任何额外的工作,但自定义类型需要更多" +"的工作。这是因为在编译时,编译器不知道如何在存储中存储自定义类型。为了解决这个" +"问题,我们需要为我们的自定义类型实现 `Store`特征。希望我们可以为我们的自定义类" +"型派生这个特征 - 除非它包含数组或字典。" -#: src/ch00/basics/storing-custom-types.md:10 +#: src/ch00/basics/storing-custom-types.md:5 msgid "" +"```rust\n" +"#[starknet::interface]\n" +"trait IStoringCustomType {\n" +" fn set_person(ref self: TContractState, person: Person);\n" +"}\n" +"\n" "// Deriving the starknet::Store trait\n" "// allows us to store the `Person` struct in the contract's storage.\n" +"#[derive(Drop, Serde, Copy, starknet::Store)]\n" +"struct Person {\n" +" age: u8,\n" +" name: felt252\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod StoringCustomType {\n" +" use super::Person;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" person: Person\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl StoringCustomType of super::IStoringCustomType {\n" +" fn set_person(ref self: ContractState, person: Person) {\n" +" self.person.write(person);\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait IStoringCustomType {\n" +" fn set_person(ref self: TContractState, person: Person);\n" +"}\n" +"\n" +"// Deriving the starknet::Store trait\n" +"// allows us to store the `Person` struct in the contract's storage.\n" +"#[derive(Drop, Serde, Copy, starknet::Store)]\n" +"struct Person {\n" +" age: u8,\n" +" name: felt252\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod StoringCustomType {\n" +" use super::Person;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" person: Person\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl StoringCustomType of super::IStoringCustomType {\n" +" fn set_person(ref self: ContractState, person: Person) {\n" +" self.person.write(person);\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch00/basics/storing-custom-types.md:37 msgid "" @@ -1111,39 +1915,105 @@ msgstr "" "com/NethermindEth/StarknetByExample/blob/main/listings/ch00-getting-started/" "storing_custom_types/src/contract.cairo) 中尝试这个合约。" +#: src/ch00/basics/custom-types-in-entrypoints.md:1 +msgid "# Custom types in entrypoints" +msgstr "# 入口点中的自定义类型" + #: src/ch00/basics/custom-types-in-entrypoints.md:3 -#, fuzzy msgid "" "Using custom types in entrypoints requires our type to implement the `Serde` " "trait. This is because when calling an entrypoint, the input is sent as an " "array of `felt252` to the entrypoint, and we need to be able to deserialize " "it into our custom type. Similarly, when returning a custom type from an " -"entrypoint, we need to be able to serialize it into an array of `felt252`. " +"entrypoint, we need to be able to serialize it into an array of `felt252`.\n" "Thankfully, we can just derive the `Serde` trait for our custom type." msgstr "" -"在入口点中使用自定义类型需要我们的类型来实现`Serde`trait。这是因为在调用入口" -"点时,输入以`felt252` 数组的形式发送到入口点,我们需要能够将其反序列化为我们" -"的自定义类型。同样,当从入口点返回自定义类型时,我们需要能够将其序列化为" -"`felt252` 数组。\n" +"在入口点中使用自定义类型需要我们的类型来实现`Serde`trait。这是因为在调用入口点" +"时,输入以`felt252` 数组的形式发送到入口点,我们需要能够将其反序列化为我们的自" +"定义类型。同样,当从入口点返回自定义类型时,我们需要能够将其序列化为`felt252` " +"数组。\n" "值得庆幸的是,我们可以为我们的自定义类型派生`Serde` 特征。" -#: src/ch00/basics/custom-types-in-entrypoints.md:18 +#: src/ch00/basics/custom-types-in-entrypoints.md:6 msgid "" -"// Deriving the `Serde` trait allows us to use\n" +"```rust\n" +"#[starknet::interface]\n" +"trait ISerdeCustomType {\n" +" fn person_input(ref self: TContractState, person: SerdeCustomType::" +"Person);\n" +" fn person_output(self: @TContractState) -> SerdeCustomType::Person;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod SerdeCustomType {\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" // Deriving the `Serde` trait allows us to use\n" " // the Person type as an entrypoint parameter and return value\n" +" #[derive(Drop, Serde)]\n" +" struct Person {\n" +" age: u8,\n" +" name: felt252\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl SerdeCustomType of super::ISerdeCustomType {\n" +" fn person_input(ref self: ContractState, person: Person) {}\n" +"\n" +" fn person_output(self: @ContractState) -> Person {\n" +" Person { age: 10, name: 'Joe' }\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait ISerdeCustomType {\n" +" fn person_input(ref self: TContractState, person: SerdeCustomType::" +"Person);\n" +" fn person_output(self: @TContractState) -> SerdeCustomType::Person;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod SerdeCustomType {\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" // Deriving the `Serde` trait allows us to use\n" +" // the Person type as an entrypoint parameter and return value\n" +" #[derive(Drop, Serde)]\n" +" struct Person {\n" +" age: u8,\n" +" name: felt252\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl SerdeCustomType of super::ISerdeCustomType {\n" +" fn person_input(ref self: ContractState, person: Person) {}\n" +"\n" +" fn person_output(self: @ContractState) -> Person {\n" +" Person { age: 10, name: 'Joe' }\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch00/basics/custom-types-in-entrypoints.md:37 msgid "" "Play with this contract in [Remix](https://remix.ethereum.org/?" "#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/" -"blob/main/listings/ch00-getting-started/custom_type_serde/src/contract." -"cairo)." +"blob/main/listings/ch00-getting-started/custom_type_serde/src/contract.cairo)." msgstr "" "在 [Remix](https://remix.ethereum.org/?#activate=Starknet&url=https://github." "com/NethermindEth/StarknetByExample/blob/main/listings/ch00-getting-started/" "custom_type_serde/src/contract.cairo) 中尝试这个合约。" +#: src/ch00/basics/documentation.md:1 +msgid "# Documentation" +msgstr "# 文档" + #: src/ch00/basics/documentation.md:3 msgid "" "It's important to take the time to document your code. It will helps " @@ -1156,8 +2026,7 @@ msgid "In Cairo, you can add comments with `//`." msgstr "在Cairo,您可以使用“//”添加注释。" #: src/ch00/basics/documentation.md:7 -#, fuzzy -msgid "Best Practices:" +msgid "### Best Practices:" msgstr "### 最佳实践:" #: src/ch00/basics/documentation.md:9 @@ -1169,27 +2038,28 @@ msgstr "" "rust-by-example/meta/doc.html)。" #: src/ch00/basics/documentation.md:11 -#, fuzzy -msgid "Contract Interface:" +msgid "### Contract Interface:" msgstr "### 合约接口:" #: src/ch00/basics/documentation.md:13 -#, fuzzy msgid "" "In smart contracts, you will often have a trait that defines the contract's " -"interface (with `#[starknet::interface]`). This is the perfect place to " -"include detailed documentation explaining the purpose and functionality of " -"the contract entry points. You can follow this template:" +"interface (with `#[starknet::interface]`).\n" +"This is the perfect place to include detailed documentation explaining the " +"purpose and functionality of the contract entry points. You can follow this " +"template:" msgstr "" "在智能合约中,你通常会有一个定义合约接口的trait(带有'#[starknet::" "interface]')。\n" -"这是包含详细文档的理想场所,这些文档解释了合约入口点的用途和功能。您可以遵循" -"以下模板:" +"这是包含详细文档的理想场所,这些文档解释了合约入口点的用途和功能。您可以遵循以" +"下模板:" -#: src/ch00/basics/documentation.md:19 -#, fuzzy +#: src/ch00/basics/documentation.md:16 msgid "" -"/// High-level description of the function\n" +"```rust\n" +"#[starknet::interface]\n" +"trait IContract {\n" +" /// High-level description of the function\n" " ///\n" " /// # Arguments\n" " ///\n" @@ -1199,6 +2069,9 @@ msgid "" " /// # Returns\n" " ///\n" " /// High-level description of the return value\n" +" fn do_something(ref self: TContractState, arg_1: T_arg_1) -> T_return;\n" +"}\n" +"```" msgstr "" "```rust\n" "#[starknet::interface]\n" @@ -1220,15 +2093,14 @@ msgstr "" #: src/ch00/basics/documentation.md:33 msgid "" "Keep in mind that this should not describe the implementation details of the " -"function, but rather the high-level purpose and functionality of the " -"contract from the perspective of a user." +"function, but rather the high-level purpose and functionality of the contract " +"from the perspective of a user." msgstr "" "请记住,这不应该描述函数的实现细节,而应该从用户的角度描述合约的高级目的和功" "能。" #: src/ch00/basics/documentation.md:35 -#, fuzzy -msgid "Implementation Details:" +msgid "### Implementation Details:" msgstr "### 实装细节:" #: src/ch00/basics/documentation.md:37 @@ -1238,15 +2110,26 @@ msgid "" msgstr "在编写合约逻辑时,可以添加注释来描述函数的技术实现细节。" #: src/ch00/basics/documentation.md:39 -#, fuzzy msgid "" -"Avoid over-commenting: Comments should provide additional value and clarity." +"> Avoid over-commenting: Comments should provide additional value and clarity." msgstr "> 避免过度注释:注释应提供额外的价值和清晰度。" +#: src/ch00/basics/documentation.md:41 +msgid "
Last change: 2023-12-05
" +msgstr "
Last change: 2023-12-05
" + +#: src/ch00/interacting/interacting.md:1 +msgid "# Deploy and interact with contracts" +msgstr "# 部署合约并与合约交互" + #: src/ch00/interacting/interacting.md:3 msgid "In this chapter, we will see how to deploy and interact with contracts." msgstr "在本章中,我们将了解如何部署合约并与之交互。" +#: src/ch00/interacting/interfaces-traits.md:1 +msgid "# Contract interfaces and Traits generation" +msgstr "# 合约接口和Trait生成" + #: src/ch00/interacting/interfaces-traits.md:3 msgid "" "Contract interfaces define the structure and behavior of a contract, serving " @@ -1255,8 +2138,8 @@ msgid "" "the [Cairo Book](https://book.cairo-lang.org/ch99-01-02-a-simple-contract." "html)." msgstr "" -"合约接口定义合约的结构和行为,充当合约的公共 ABI。它们列出了合约公开的所有函" -"数签名。接口的详细说明可以参考 [Cairo之书](https://book.cairo-lang.org/" +"合约接口定义合约的结构和行为,充当合约的公共 ABI。它们列出了合约公开的所有函数" +"签名。接口的详细说明可以参考 [Cairo之书](https://book.cairo-lang.org/" "ch99-01-02-a-simple-contract.html)。" #: src/ch00/interacting/interfaces-traits.md:5 @@ -1291,23 +2174,19 @@ msgid "" "l1 handler." msgstr "" "您可以使用`#[generate_trait]`属性隐式生成特定实现块的特征。此属性会自动生成一" -"个特征,其功能与已实现块中的函数相同,将`self`参数替换为通用的" -"`TContractState`参数。但是,您需要使用`#[abi(per_item)]` 属性注释块,并且每个" -"函数都具有适当的属性,具体取决于它是外部函数、构造函数还是 l1 处理程序。" +"个特征,其功能与已实现块中的函数相同,将`self`参数替换为通用的`TContractState`" +"参数。但是,您需要使用`#[abi(per_item)]` 属性注释块,并且每个函数都具有适当的" +"属性,具体取决于它是外部函数、构造函数还是 l1 处理程序。" #: src/ch00/interacting/interfaces-traits.md:11 msgid "In summary, there's two ways to handle interfaces:" msgstr "总之,有两种方法可以处理接口:" #: src/ch00/interacting/interfaces-traits.md:13 -msgid "Explicitly, by defining a trait annoted with `#[starknet::interface]`" -msgstr "" - -#: src/ch00/interacting/interfaces-traits.md:14 -#, fuzzy msgid "" -"Implicitly, by using `#[generate_trait]` combined with the " -"#\\[abi(per_item)\\]\\` attributes, and annotating each function inside the " +"- Explicitly, by defining a trait annoted with `#[starknet::interface]`\n" +"- Implicitly, by using `#[generate_trait]` combined with the " +"#[abi(per_item)]` attributes, and annotating each function inside the " "implementation block with the appropriate attribute." msgstr "" "- 显示地,通过定义一个用`#[starknet::interface]`标记的特征\n" @@ -1315,62 +2194,257 @@ msgstr "" "当的属性注释实现块中的每个函数。" #: src/ch00/interacting/interfaces-traits.md:16 -#, fuzzy -msgid "Explicit interface" +msgid "## Explicit interface" msgstr "## 显式接口" +#: src/ch00/interacting/interfaces-traits.md:18 +msgid "" +"```rust\n" +"#[starknet::interface]\n" +"trait IExplicitInterfaceContract {\n" +" fn get_value(self: @TContractState) -> u32;\n" +" fn set_value(ref self: TContractState, value: u32);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod ExplicitInterfaceContract {\n" +" #[storage]\n" +" struct Storage {\n" +" value: u32\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ExplicitInterfaceContract of super::" +"IExplicitInterfaceContract {\n" +" fn get_value(self: @ContractState) -> u32 {\n" +" self.value.read()\n" +" }\n" +"\n" +" fn set_value(ref self: ContractState, value: u32) {\n" +" self.value.write(value);\n" +" }\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait IExplicitInterfaceContract {\n" +" fn get_value(self: @TContractState) -> u32;\n" +" fn set_value(ref self: TContractState, value: u32);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod ExplicitInterfaceContract {\n" +" #[storage]\n" +" struct Storage {\n" +" value: u32\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ExplicitInterfaceContract of super::" +"IExplicitInterfaceContract {\n" +" fn get_value(self: @ContractState) -> u32 {\n" +" self.value.read()\n" +" }\n" +"\n" +" fn set_value(ref self: ContractState, value: u32) {\n" +" self.value.write(value);\n" +" }\n" +" }\n" +"}\n" +"```" + #: src/ch00/interacting/interfaces-traits.md:45 msgid "" "Play with this contract in [Remix](https://remix.ethereum.org/?" "#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/" -"blob/main/listings/ch00-getting-started/interfaces_traits/src/explicit." -"cairo)." +"blob/main/listings/ch00-getting-started/interfaces_traits/src/explicit.cairo)." msgstr "" "在 [Remix](https://remix.ethereum.org/?#activate=Starknet&url=https://github." "com/NethermindEth/StarknetByExample/blob/main/listings/ch00-getting-started/" "interfaces_traits/src/explicit.cairo) 中尝试这个合约。" #: src/ch00/interacting/interfaces-traits.md:47 -#, fuzzy -msgid "Implicit interface" +msgid "## Implicit interface" msgstr "## 隐式接口" +#: src/ch00/interacting/interfaces-traits.md:49 +msgid "" +"```rust\n" +"#[starknet::contract]\n" +"mod ImplicitInterfaceContract {\n" +" #[storage]\n" +" struct Storage {\n" +" value: u32\n" +" }\n" +"\n" +" #[abi(per_item)]\n" +" #[generate_trait]\n" +" impl ImplicitInterfaceContract of IImplicitInterfaceContract {\n" +" #[external(v0)]\n" +" fn get_value(self: @ContractState) -> u32 {\n" +" self.value.read()\n" +" }\n" +"\n" +" #[external(v0)]\n" +" fn set_value(ref self: ContractState, value: u32) {\n" +" self.value.write(value);\n" +" }\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"#[starknet::contract]\n" +"mod ImplicitInterfaceContract {\n" +" #[storage]\n" +" struct Storage {\n" +" value: u32\n" +" }\n" +"\n" +" #[abi(per_item)]\n" +" #[generate_trait]\n" +" impl ImplicitInterfaceContract of IImplicitInterfaceContract {\n" +" #[external(v0)]\n" +" fn get_value(self: @ContractState) -> u32 {\n" +" self.value.read()\n" +" }\n" +"\n" +" #[external(v0)]\n" +" fn set_value(ref self: ContractState, value: u32) {\n" +" self.value.write(value);\n" +" }\n" +" }\n" +"}\n" +"```" + #: src/ch00/interacting/interfaces-traits.md:73 msgid "" "Play with this contract in [Remix](https://remix.ethereum.org/?" "#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/" -"blob/main/listings/ch00-getting-started/interfaces_traits/src/implicit." -"cairo)." +"blob/main/listings/ch00-getting-started/interfaces_traits/src/implicit.cairo)." msgstr "" "在 [Remix](https://remix.ethereum.org/?#activate=Starknet&url=https://github." "com/NethermindEth/StarknetByExample/blob/main/listings/ch00-getting-started/" "interfaces_traits/src/implicit.cairo) 中尝试这个合约。" #: src/ch00/interacting/interfaces-traits.md:75 -#, fuzzy msgid "" -"Note: You can import an implicitly generated contract interface with `use " +"> Note: You can import an implicitly generated contract interface with `use " "contract::{GeneratedContractInterface}`. However, the `Dispatcher` will not " "be generated automatically." msgstr "" -"> 注意:您可以使用`use contract::{GeneratedContractInterface}`导入隐式生成的" -"合约接口。但是,`Dispatcher`不会自动生成。" +"> 注意:您可以使用`use contract::{GeneratedContractInterface}`导入隐式生成的合" +"约接口。但是,`Dispatcher`不会自动生成。" #: src/ch00/interacting/interfaces-traits.md:77 -#, fuzzy -msgid "Internal functions" +msgid "## Internal functions" msgstr "## 内部函数" #: src/ch00/interacting/interfaces-traits.md:79 -#, fuzzy msgid "" -"You can also use `#[generate_trait]` for your internal functions. Since this " -"trait is generated in the context of the contract, you can define pure " -"functions as well (functions without the `self` parameter)." +"You can also use `#[generate_trait]` for your internal functions.\n" +"Since this trait is generated in the context of the contract, you can define " +"pure functions as well (functions without the `self` parameter)." msgstr "" "您还可以将`#[generate_trait]`用于内部函数。\n" -"由于此特征是在合约的上下文中生成的,因此您也可以定义纯函数(没有“self”参数的" -"函数)。" +"由于此特征是在合约的上下文中生成的,因此您也可以定义纯函数(没有“self”参数的函" +"数)。" + +#: src/ch00/interacting/interfaces-traits.md:82 +msgid "" +"```rust\n" +"#[starknet::interface]\n" +"trait IImplicitInternalContract {\n" +" fn add(ref self: TContractState, nb: u32);\n" +" fn get_value(self: @TContractState) -> u32;\n" +" fn get_const(self: @TContractState) -> u32;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod ImplicitInternalContract {\n" +" #[storage]\n" +" struct Storage {\n" +" value: u32\n" +" }\n" +"\n" +" #[generate_trait]\n" +" impl InternalFunctions of InternalFunctionsTrait {\n" +" fn set_value(ref self: ContractState, value: u32) {\n" +" self.value.write(value);\n" +" }\n" +" fn get_const() -> u32 {\n" +" 42\n" +" }\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(ref self: ContractState) {\n" +" self.set_value(0);\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ImplicitInternalContract of super::" +"IImplicitInternalContract {\n" +" fn add(ref self: ContractState, nb: u32) {\n" +" self.set_value(self.value.read() + nb);\n" +" }\n" +" fn get_value(self: @ContractState) -> u32 {\n" +" self.value.read()\n" +" }\n" +" fn get_const(self: @ContractState) -> u32 {\n" +" self.get_const()\n" +" }\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait IImplicitInternalContract {\n" +" fn add(ref self: TContractState, nb: u32);\n" +" fn get_value(self: @TContractState) -> u32;\n" +" fn get_const(self: @TContractState) -> u32;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod ImplicitInternalContract {\n" +" #[storage]\n" +" struct Storage {\n" +" value: u32\n" +" }\n" +"\n" +" #[generate_trait]\n" +" impl InternalFunctions of InternalFunctionsTrait {\n" +" fn set_value(ref self: ContractState, value: u32) {\n" +" self.value.write(value);\n" +" }\n" +" fn get_const() -> u32 {\n" +" 42\n" +" }\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(ref self: ContractState) {\n" +" self.set_value(0);\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ImplicitInternalContract of super::" +"IImplicitInternalContract {\n" +" fn add(ref self: ContractState, nb: u32) {\n" +" self.set_value(self.value.read() + nb);\n" +" }\n" +" fn get_value(self: @ContractState) -> u32 {\n" +" self.value.read()\n" +" }\n" +" fn get_const(self: @ContractState) -> u32 {\n" +" self.get_const()\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch00/interacting/interfaces-traits.md:127 msgid "" @@ -1383,22 +2457,30 @@ msgstr "" "com/NethermindEth/StarknetByExample/blob/main/listings/ch00-getting-started/" "interfaces_traits/src/implicit_internal.cairo) 中尝试这个合约。" +#: src/ch00/interacting/interfaces-traits.md:129 +msgid "
Last change: 2023-11-26
" +msgstr "
Last change: 2023-11-26
" + +#: src/ch00/interacting/calling_other_contracts.md:1 +msgid "# Calling other contracts" +msgstr "# 调用其他合约" + #: src/ch00/interacting/calling_other_contracts.md:3 msgid "There are two different ways to call other contracts in Cairo." msgstr "在Cairo,有两种不同的方式可以调用其他合约。" #: src/ch00/interacting/calling_other_contracts.md:5 -#, fuzzy msgid "" "The easiest way to call other contracts is by using the dispatcher of the " -"contract you want to call. You can read more about Dispatchers in the [Cairo " -"Book](https://book.cairo-lang.org/ch99-02-02-contract-dispatcher-library-" -"dispatcher-and-system-calls.html#contract-dispatcher)" +"contract you want to call.\n" +"You can read more about Dispatchers in the [Cairo Book](https://book.cairo-" +"lang.org/ch99-02-02-contract-dispatcher-library-dispatcher-and-system-calls." +"html#contract-dispatcher)" msgstr "" "调用其他合约的最简单方法是使用要调用的合约的调度程序。\n" "您可以在 [Cairo Book](https://book.cairo-lang.org/ch99-02-02-contract-" -"dispatcher-library-dispatcher-and-system-calls.html#contract-dispatcher) 中" -"阅读有关 Dispatchers 的更多信息" +"dispatcher-library-dispatcher-and-system-calls.html#contract-dispatcher) 中阅" +"读有关 Dispatchers 的更多信息" #: src/ch00/interacting/calling_other_contracts.md:8 msgid "" @@ -1415,10 +2497,58 @@ msgid "" "interface]` attribute, and then import the `IContractDispatcher` and " "`IContractDispatcherTrait` items in your contract." msgstr "" -"为了使用调度程序调用其他合约,您需要将被调用合约的接口定义为使用 " -"`#[starknet::interface]` 属性注释的trait,然后将 `IContractDispatcher` 和 " +"为了使用调度程序调用其他合约,您需要将被调用合约的接口定义为使用 `#[starknet::" +"interface]` 属性注释的trait,然后将 `IContractDispatcher` 和 " "`IContractDispatcherTrait` 项导入到合约中。" +#: src/ch00/interacting/calling_other_contracts.md:12 +msgid "" +"```rust\n" +"#[starknet::interface]\n" +"trait ICallee {\n" +" fn set_value(ref self: TContractState, value: u128) -> u128;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod Callee {\n" +" #[storage]\n" +" struct Storage {\n" +" value: u128,\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ICalleeImpl of super::ICallee {\n" +" fn set_value(ref self: ContractState, value: u128) -> u128 {\n" +" self.value.write(value);\n" +" value\n" +" }\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait ICallee {\n" +" fn set_value(ref self: TContractState, value: u128) -> u128;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod Callee {\n" +" #[storage]\n" +" struct Storage {\n" +" value: u128,\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ICalleeImpl of super::ICallee {\n" +" fn set_value(ref self: ContractState, value: u128) -> u128 {\n" +" self.value.write(value);\n" +" value\n" +" }\n" +" }\n" +"}\n" +"```" + #: src/ch00/interacting/calling_other_contracts.md:34 msgid "" "Visit contract on [Voyager](https://goerli.voyager.online/" @@ -1435,23 +2565,85 @@ msgstr "" "blob/main/listings/ch00-getting-started/calling_other_contracts/src/callee." "cairo)中尝试它。" -#: src/ch00/interacting/calling_other_contracts.md:38 +#: src/ch00/interacting/calling_other_contracts.md:36 msgid "" +"```rust\n" +"use starknet::ContractAddress;\n" +"\n" "// We need to have the interface of the callee contract defined\n" "// so that we can import the Dispatcher.\n" +"#[starknet::interface]\n" +"trait ICallee {\n" +" fn set_value(ref self: TContractState, value: u128) -> u128;\n" +"}\n" +"\n" +"#[starknet::interface]\n" +"trait ICaller {\n" +" fn set_value_from_address(ref self: TContractState, addr: " +"ContractAddress, value: u128);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod Caller {\n" +" // We import the Dispatcher of the called contract\n" +" use super::{ICalleeDispatcher, ICalleeDispatcherTrait};\n" +" use starknet::ContractAddress;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ICallerImpl of super::ICaller {\n" +" fn set_value_from_address(ref self: ContractState, addr: " +"ContractAddress, value: u128) {\n" +" ICalleeDispatcher { contract_address: addr }.set_value(value);\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" - -#: src/ch00/interacting/calling_other_contracts.md:53 -msgid "// We import the Dispatcher of the called contract\n" -msgstr "" - -#: src/ch00/interacting/calling_other_contracts.md:68 -msgid "" -"Visit contract on [Voyager](https://goerli.voyager.online/" -"contract/0x05fa8aF796343d2f22c53C17149386b67B7AC4aB52D9e308Aa507C185aA44778) " -"or play with it in [Remix](https://remix.ethereum.org/?" -"#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/" -"blob/main/listings/ch00-getting-started/calling_other_contracts/src/caller." +"```rust\n" +"use starknet::ContractAddress;\n" +"\n" +"// We need to have the interface of the callee contract defined\n" +"// so that we can import the Dispatcher.\n" +"#[starknet::interface]\n" +"trait ICallee {\n" +" fn set_value(ref self: TContractState, value: u128) -> u128;\n" +"}\n" +"\n" +"#[starknet::interface]\n" +"trait ICaller {\n" +" fn set_value_from_address(ref self: TContractState, addr: " +"ContractAddress, value: u128);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod Caller {\n" +" // We import the Dispatcher of the called contract\n" +" use super::{ICalleeDispatcher, ICalleeDispatcherTrait};\n" +" use starknet::ContractAddress;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ICallerImpl of super::ICaller {\n" +" fn set_value_from_address(ref self: ContractState, addr: " +"ContractAddress, value: u128) {\n" +" ICalleeDispatcher { contract_address: addr }.set_value(value);\n" +" }\n" +" }\n" +"}\n" +"```" + +#: src/ch00/interacting/calling_other_contracts.md:68 +msgid "" +"Visit contract on [Voyager](https://goerli.voyager.online/" +"contract/0x05fa8aF796343d2f22c53C17149386b67B7AC4aB52D9e308Aa507C185aA44778) " +"or play with it in [Remix](https://remix.ethereum.org/?" +"#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/" +"blob/main/listings/ch00-getting-started/calling_other_contracts/src/caller." "cairo)." msgstr "" "在 [Voyager](https://goerli.voyager.online/" @@ -1462,8 +2654,7 @@ msgstr "" "cairo) 中尝试它。" #: src/ch00/interacting/factory.md:1 -#, fuzzy -msgid "Factory Pattern" +msgid "# Factory Pattern" msgstr "# 工厂模式" #: src/ch00/interacting/factory.md:3 @@ -1475,38 +2666,37 @@ msgstr "" #: src/ch00/interacting/factory.md:5 msgid "" -"In the case of smart contracts, we can use this pattern by defining a " -"factory contract that have the sole responsibility of creating and managing " -"other contracts." +"In the case of smart contracts, we can use this pattern by defining a factory " +"contract that have the sole responsibility of creating and managing other " +"contracts." msgstr "" -"在智能合约里,我们可以通过定义一个工厂合约来使用这种模式,该合约全权负责创建" -"和管理其他合约。" +"在智能合约里,我们可以通过定义一个工厂合约来使用这种模式,该合约全权负责创建和" +"管理其他合约。" #: src/ch00/interacting/factory.md:7 -#, fuzzy -msgid "Class hash and contract instance" +msgid "## Class hash and contract instance" msgstr "## 类哈希(Class hash)和合约实例" #: src/ch00/interacting/factory.md:9 msgid "" -"In Starknet, there's a separation between contract's classes and instances. " -"A contract class serves as a blueprint, defined by the underling Cairo " +"In Starknet, there's a separation between contract's classes and instances. A " +"contract class serves as a blueprint, defined by the underling Cairo " "bytecode, contract's entrypoints, ABI and Sierra program hash. The contract " "class is identified by a class hash. When you want to add a new class to the " "network, you first need to declare it." msgstr "" "在Starknet中,合约的类和实例是分开的。合约类充当蓝图,由底层 Cairo 字节码、合" -"约的入口点、ABI 和 Sierra 程序哈希定义。合约类由类哈希标识。当您想向网络添加" -"一个新类时,首先需要声明它。" +"约的入口点、ABI 和 Sierra 程序哈希定义。合约类由类哈希标识。当您想向网络添加一" +"个新类时,首先需要声明它。" #: src/ch00/interacting/factory.md:11 msgid "" -"When deploying a contract, you need to specify the class hash of the " -"contract you want to deploy. Each instance of a contract has their own " -"storage regardless of the class hash." +"When deploying a contract, you need to specify the class hash of the contract " +"you want to deploy. Each instance of a contract has their own storage " +"regardless of the class hash." msgstr "" -"部署合约时,需要指定要部署的合约的类哈希值。合约的每个实例都有自己的存储,这" -"与类哈希无关。" +"部署合约时,需要指定要部署的合约的类哈希值。合约的每个实例都有自己的存储,这与" +"类哈希无关。" #: src/ch00/interacting/factory.md:13 msgid "" @@ -1515,8 +2705,7 @@ msgid "" msgstr "使用工厂模式,我们可以部署同一合约类的多个实例,并轻松处理升级。" #: src/ch00/interacting/factory.md:15 -#, fuzzy -msgid "Minimal example" +msgid "## Minimal example" msgstr "## 最小范例" #: src/ch00/interacting/factory.md:17 @@ -1525,39 +2714,165 @@ msgid "" "`SimpleCounter` contract:" msgstr "下面是部署`SimpleCounter` 合约的工厂合约的最小范例:" -#: src/ch00/interacting/factory.md:24 -msgid "/// Create a new counter contract from stored arguments\n" -msgstr "" - -#: src/ch00/interacting/factory.md:27 -msgid "/// Create a new counter contract from the given arguments\n" -msgstr "" - -#: src/ch00/interacting/factory.md:30 -msgid "/// Update the argument\n" -msgstr "" - -#: src/ch00/interacting/factory.md:33 +#: src/ch00/interacting/factory.md:19 msgid "" -"/// Update the class hash of the Counter contract to deploy when creating a " -"new counter\n" -msgstr "" - -#: src/ch00/interacting/factory.md:44 -msgid "/// Store the constructor arguments of the contract to deploy\n" -msgstr "" - -#: src/ch00/interacting/factory.md:46 -msgid "/// Store the class hash of the contract to deploy\n" -msgstr "" - -#: src/ch00/interacting/factory.md:59 -msgid "// Contructor arguments\n" +"```rust\n" +"use starknet::{ContractAddress, ClassHash};\n" +"\n" +"#[starknet::interface]\n" +"trait ICounterFactory {\n" +" /// Create a new counter contract from stored arguments\n" +" fn create_counter(ref self: TContractState) -> ContractAddress;\n" +"\n" +" /// Create a new counter contract from the given arguments\n" +" fn create_counter_at(ref self: TContractState, init_value: u128) -> " +"ContractAddress;\n" +"\n" +" /// Update the argument\n" +" fn update_init_value(ref self: TContractState, init_value: u128);\n" +"\n" +" /// Update the class hash of the Counter contract to deploy when creating " +"a new counter\n" +" fn update_counter_class_hash(ref self: TContractState, " +"counter_class_hash: ClassHash);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod CounterFactory {\n" +" use starknet::{ContractAddress, ClassHash};\n" +" use starknet::syscalls::deploy_syscall;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" /// Store the constructor arguments of the contract to deploy\n" +" init_value: u128,\n" +" /// Store the class hash of the contract to deploy\n" +" counter_class_hash: ClassHash,\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(ref self: ContractState, init_value: u128, class_hash: " +"ClassHash) {\n" +" self.init_value.write(init_value);\n" +" self.counter_class_hash.write(class_hash);\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl Factory of super::ICounterFactory {\n" +" fn create_counter_at(ref self: ContractState, init_value: u128) -> " +"ContractAddress {\n" +" // Contructor arguments\n" +" let mut constructor_calldata: Array:: = array!" +"[init_value.into()];\n" +"\n" +" // Contract deployment\n" +" let (deployed_address, _) = deploy_syscall(\n" +" self.counter_class_hash.read(), 0, constructor_calldata." +"span(), false\n" +" )\n" +" .expect('failed to deploy counter');\n" +"\n" +" deployed_address\n" +" }\n" +"\n" +" fn create_counter(ref self: ContractState) -> ContractAddress {\n" +" self.create_counter_at(self.init_value.read())\n" +" }\n" +"\n" +" fn update_init_value(ref self: ContractState, init_value: u128) {\n" +" self.init_value.write(init_value);\n" +" }\n" +"\n" +" fn update_counter_class_hash(ref self: ContractState, " +"counter_class_hash: ClassHash) {\n" +" self.counter_class_hash.write(counter_class_hash);\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"use starknet::{ContractAddress, ClassHash};\n" +"\n" +"#[starknet::interface]\n" +"trait ICounterFactory {\n" +" /// Create a new counter contract from stored arguments\n" +" fn create_counter(ref self: TContractState) -> ContractAddress;\n" +"\n" +" /// Create a new counter contract from the given arguments\n" +" fn create_counter_at(ref self: TContractState, init_value: u128) -> " +"ContractAddress;\n" +"\n" +" /// Update the argument\n" +" fn update_init_value(ref self: TContractState, init_value: u128);\n" +"\n" +" /// Update the class hash of the Counter contract to deploy when creating " +"a new counter\n" +" fn update_counter_class_hash(ref self: TContractState, " +"counter_class_hash: ClassHash);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod CounterFactory {\n" +" use starknet::{ContractAddress, ClassHash};\n" +" use starknet::syscalls::deploy_syscall;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" /// Store the constructor arguments of the contract to deploy\n" +" init_value: u128,\n" +" /// Store the class hash of the contract to deploy\n" +" counter_class_hash: ClassHash,\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(ref self: ContractState, init_value: u128, class_hash: " +"ClassHash) {\n" +" self.init_value.write(init_value);\n" +" self.counter_class_hash.write(class_hash);\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl Factory of super::ICounterFactory {\n" +" fn create_counter_at(ref self: ContractState, init_value: u128) -> " +"ContractAddress {\n" +" // Contructor arguments\n" +" let mut constructor_calldata: Array:: = array!" +"[init_value.into()];\n" +"\n" +" // Contract deployment\n" +" let (deployed_address, _) = deploy_syscall(\n" +" self.counter_class_hash.read(), 0, constructor_calldata." +"span(), false\n" +" )\n" +" .expect('failed to deploy counter');\n" +"\n" +" deployed_address\n" +" }\n" +"\n" +" fn create_counter(ref self: ContractState) -> ContractAddress {\n" +" self.create_counter_at(self.init_value.read())\n" +" }\n" +"\n" +" fn update_init_value(ref self: ContractState, init_value: u128) {\n" +" self.init_value.write(init_value);\n" +" }\n" +"\n" +" fn update_counter_class_hash(ref self: ContractState, " +"counter_class_hash: ClassHash) {\n" +" self.counter_class_hash.write(counter_class_hash);\n" +" }\n" +" }\n" +"}\n" +"```" -#: src/ch00/interacting/factory.md:62 -msgid "// Contract deployment\n" +#: src/ch00/interacting/factory.md:86 +msgid "" +"\n" +"\n" +"
Last change: 2023-10-19
" +msgstr "" +"\n" +"\n" +"
Last change: 2023-10-19
" + #: src/ch00/testing/contract-testing.md:1 -#, fuzzy -msgid "Contract Testing" +msgid "# Contract Testing" msgstr "# 合约测试" #: src/ch00/testing/contract-testing.md:3 @@ -1594,57 +2920,282 @@ msgid "" "contracts. In this section, we'll guide you through the basics of testing a " "smart contract on Starknet with `scarb`." msgstr "" -"测试在软件开发中起着至关重要的作用,尤其是对于智能合约而言。在本节中,我们将" -"通过Starknet上的`scarb` ,引导你了解智能合约测试的基础知识。" +"测试在软件开发中起着至关重要的作用,尤其是对于智能合约而言。在本节中,我们将通" +"过Starknet上的`scarb` ,引导你了解智能合约测试的基础知识。" #: src/ch00/testing/contract-testing.md:5 msgid "Let's start with a simple smart contract as an example:" msgstr "让我们以一个简单的智能合约作为例子开始:" +#: src/ch00/testing/contract-testing.md:6 +msgid "" +"```rust\n" +"use starknet::ContractAddress;\n" +"\n" +"#[starknet::interface]\n" +"trait ISimpleContract {\n" +" fn get_value(self: @TContractState) -> u32;\n" +" fn get_owner(self: @TContractState) -> ContractAddress;\n" +" fn set_value(ref self: TContractState, value: u32);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod SimpleContract {\n" +" use starknet::{get_caller_address, ContractAddress};\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" value: u32,\n" +" owner: ContractAddress\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(ref self: ContractState, initial_value: u32) {\n" +" self.value.write(initial_value);\n" +" self.owner.write(get_caller_address());\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl SimpleContract of super::ISimpleContract {\n" +" fn get_value(self: @ContractState) -> u32 {\n" +" self.value.read()\n" +" }\n" +"\n" +" fn get_owner(self: @ContractState) -> ContractAddress {\n" +" self.owner.read()\n" +" }\n" +"\n" +" fn set_value(ref self: ContractState, value: u32) {\n" +" assert(self.owner.read() == get_caller_address(), 'Not owner');\n" +" self.value.write(value);\n" +" }\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"use starknet::ContractAddress;\n" +"\n" +"#[starknet::interface]\n" +"trait ISimpleContract {\n" +" fn get_value(self: @TContractState) -> u32;\n" +" fn get_owner(self: @TContractState) -> ContractAddress;\n" +" fn set_value(ref self: TContractState, value: u32);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod SimpleContract {\n" +" use starknet::{get_caller_address, ContractAddress};\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" value: u32,\n" +" owner: ContractAddress\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(ref self: ContractState, initial_value: u32) {\n" +" self.value.write(initial_value);\n" +" self.owner.write(get_caller_address());\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl SimpleContract of super::ISimpleContract {\n" +" fn get_value(self: @ContractState) -> u32 {\n" +" self.value.read()\n" +" }\n" +"\n" +" fn get_owner(self: @ContractState) -> ContractAddress {\n" +" self.owner.read()\n" +" }\n" +"\n" +" fn set_value(ref self: ContractState, value: u32) {\n" +" assert(self.owner.read() == get_caller_address(), ‘Not owner’);\n" +" self.value.write(value);\n" +" }\n" +" }\n" +"}\n" +"```" + #: src/ch00/testing/contract-testing.md:50 msgid "Now, take a look at the tests for this contract:" msgstr "现在,让我们看一下这个合约的测试:" -#: src/ch00/testing/contract-testing.md:54 +#: src/ch00/testing/contract-testing.md:51 msgid "" -"// Import the interface and dispatcher to be able to interact with the " +"```rust\n" +"#[cfg(test)]\n" +"mod tests {\n" +" // Import the interface and dispatcher to be able to interact with the " "contract.\n" -msgstr "" - -#: src/ch00/testing/contract-testing.md:59 -msgid "// Import the deploy syscall to be able to deploy the contract.\n" -msgstr "" - -#: src/ch00/testing/contract-testing.md:66 -msgid "// Use starknet test utils to fake the transaction context.\n" -msgstr "" - -#: src/ch00/testing/contract-testing.md:69 -msgid "// Deploy the contract and return its dispatcher.\n" -msgstr "" - -#: src/ch00/testing/contract-testing.md:71 -msgid "// Set up constructor arguments.\n" -msgstr "" - -#: src/ch00/testing/contract-testing.md:75 -msgid "// Declare and deploy\n" -msgstr "" - -#: src/ch00/testing/contract-testing.md:81 -msgid "" -"// Return the dispatcher.\n" +" use sbe_testing::contract::{\n" +" ISimpleContract, SimpleContract, ISimpleContractDispatcher, " +"ISimpleContractDispatcherTrait\n" +" };\n" +"\n" +" // Import the deploy syscall to be able to deploy the contract.\n" +" use starknet::class_hash::Felt252TryIntoClassHash;\n" +" use starknet::{\n" +" deploy_syscall, ContractAddress, get_caller_address, " +"get_contract_address,\n" +" contract_address_const\n" +" };\n" +"\n" +" // Use starknet test utils to fake the transaction context.\n" +" use starknet::testing::{set_caller_address, set_contract_address};\n" +"\n" +" // Deploy the contract and return its dispatcher.\n" +" fn deploy(initial_value: u32) -> ISimpleContractDispatcher {\n" +" // Set up constructor arguments.\n" +" let mut calldata = ArrayTrait::new();\n" +" initial_value.serialize(ref calldata);\n" +"\n" +" // Declare and deploy\n" +" let (contract_address, _) = deploy_syscall(\n" +" SimpleContract::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata." +"span(), false\n" +" )\n" +" .unwrap();\n" +"\n" +" // Return the dispatcher.\n" " // The dispatcher allows to interact with the contract based on its " "interface.\n" +" ISimpleContractDispatcher { contract_address }\n" +" }\n" +"\n" +" #[test]\n" +" #[available_gas(2000000000)]\n" +" fn test_deploy() {\n" +" let initial_value: u32 = 10;\n" +" let contract = deploy(initial_value);\n" +"\n" +" assert(contract.get_value() == initial_value, 'wrong initial " +"value');\n" +" assert(contract.get_owner() == get_contract_address(), 'wrong " +"owner');\n" +" }\n" +"\n" +" #[test]\n" +" #[available_gas(2000000000)]\n" +" fn test_set_as_owner() {\n" +" // Fake the caller address to address 1\n" +" let owner = contract_address_const::<1>();\n" +" set_contract_address(owner);\n" +"\n" +" let contract = deploy(10);\n" +" assert(contract.get_owner() == owner, 'wrong owner');\n" +"\n" +" // Fake the contract address to address 1\n" +" set_contract_address(owner);\n" +" let new_value: u32 = 20;\n" +" contract.set_value(new_value);\n" +"\n" +" assert(contract.get_value() == new_value, 'wrong value');\n" +" }\n" +"\n" +" #[test]\n" +" #[should_panic]\n" +" #[available_gas(2000000000)]\n" +" fn test_set_not_owner() {\n" +" let owner = contract_address_const::<1>();\n" +" set_contract_address(owner);\n" +"\n" +" let contract = deploy(10);\n" +"\n" +" let not_owner = contract_address_const::<2>();\n" +" set_contract_address(not_owner);\n" +"\n" +" let new_value: u32 = 20;\n" +" contract.set_value(new_value);\n" +" }\n" +"}\n" +"```" msgstr "" - -#: src/ch00/testing/contract-testing.md:99 -msgid "// Fake the caller address to address 1\n" -msgstr "" - -#: src/ch00/testing/contract-testing.md:106 -msgid "// Fake the contract address to address 1\n" -msgstr "" +"```rust\n" +"#[cfg(test)]\n" +"mod tests {\n" +" // 导入接口和调度器,以便能够与合约进行交互\n" +" use sbe_testing::contract::{\n" +" ISimpleContract, SimpleContract, ISimpleContractDispatcher, " +"ISimpleContractDispatcherTrait\n" +" };\n" +"\n" +" //导入部署 deploy_syscall,以便能够部署合约\n" +" use starknet::class_hash::Felt252TryIntoClassHash;\n" +" use starknet::{\n" +" deploy_syscall, ContractAddress, get_caller_address, " +"get_contract_address,\n" +" contract_address_const\n" +" };\n" +"\n" +" // 使用 starknet 测试工具来模拟上下文交易\n" +" use starknet::testing::{set_caller_address, set_contract_address};\n" +"\n" +" // 部署合约并返回其调度器\n" +" fn deploy(initial_value: u32) -> ISimpleContractDispatcher {\n" +" // Set up constructor arguments.\n" +" let mut calldata = ArrayTrait::new();\n" +" initial_value.serialize(ref calldata);\n" +"\n" +" // 声明并部署合约\n" +" let (contract_address, _) = deploy_syscall(\n" +" SimpleContract::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata." +"span(), false\n" +" )\n" +" .unwrap();\n" +"\n" +" // 返回调度器\n" +" // 调度器允许根据合约的接口与其进行交互\n" +" ISimpleContractDispatcher { contract_address }\n" +" }\n" +"\n" +" #[test]\n" +" #[available_gas(2000000000)]\n" +" fn test_deploy() {\n" +" let initial_value: u32 = 10;\n" +" let contract = deploy(initial_value);\n" +"\n" +" assert(contract.get_value() == initial_value, ‘wrong initial " +"value’);\n" +" assert(contract.get_owner() == get_contract_address(), ‘wrong " +"owner’);\n" +" }\n" +"\n" +" #[test]\n" +" #[available_gas(2000000000)]\n" +" fn test_set_as_owner() {\n" +" // 将调用者地址设置为地址1\n" +" let owner = contract_address_const::<1>();\n" +" set_contract_address(owner);\n" +"\n" +" let contract = deploy(10);\n" +" assert(contract.get_owner() == owner, ‘wrong owner’);\n" +"\n" +" // 将合约地址设置为地址1\n" +" set_contract_address(owner);\n" +" let new_value: u32 = 20;\n" +" contract.set_value(new_value);\n" +"\n" +" assert(contract.get_value() == new_value, ‘wrong value’);\n" +" }\n" +"\n" +" #[test]\n" +" #[should_panic]\n" +" #[available_gas(2000000000)]\n" +" fn test_set_not_owner() {\n" +" let owner = contract_address_const::<1>();\n" +" set_contract_address(owner);\n" +"\n" +" let contract = deploy(10);\n" +"\n" +" let not_owner = contract_address_const::<2>();\n" +" set_contract_address(not_owner);\n" +"\n" +" let new_value: u32 = 20;\n" +" contract.set_value(new_value);\n" +" }\n" +"}\n" +"```" #: src/ch00/testing/contract-testing.md:132 msgid "" @@ -1658,20 +3209,20 @@ msgstr "" #: src/ch00/testing/contract-testing.md:134 msgid "" -"To define our test, we use scarb, which allows us to create a separate " -"module guarded with `#[cfg(test)]`. This ensures that the test module is " -"only compiled when running tests using `scarb test`." +"To define our test, we use scarb, which allows us to create a separate module " +"guarded with `#[cfg(test)]`. This ensures that the test module is only " +"compiled when running tests using `scarb test`." msgstr "" -"为了定义我们的测试,我们使用 scarb,它允许我们创建一个被 `#[cfg(test)]` 保护" -"的独立模块。这样可以确保测试模块只在使用 `scarb test` 运行测试时被编译。" +"为了定义我们的测试,我们使用 scarb,它允许我们创建一个被 `#[cfg(test)]` 保护的" +"独立模块。这样可以确保测试模块只在使用 `scarb test` 运行测试时被编译。" #: src/ch00/testing/contract-testing.md:136 msgid "" -"Each test is defined as a function with the `#[test]` attribute. You can " -"also check if a test panics using the `#[should_panic]` attribute." +"Each test is defined as a function with the `#[test]` attribute. You can also " +"check if a test panics using the `#[should_panic]` attribute." msgstr "" -"每个测试都被定义为带有 `#[test]` 属性的函数。您还可以使用 `#[should_panic]` " -"属性检查测试是否会引发 panic。" +"每个测试都被定义为带有 `#[test]` 属性的函数。您还可以使用 `#[should_panic]` 属" +"性检查测试是否会引发 panic。" #: src/ch00/testing/contract-testing.md:138 msgid "" @@ -1685,8 +3236,7 @@ msgstr "" "特定 gas 限制下的好方法!" #: src/ch00/testing/contract-testing.md:140 -#, fuzzy -msgid "Note: The term \"gas\" here refers to Sierra gas, not L1 gas" +msgid "> Note: The term \"gas\" here refers to Sierra gas, not L1 gas" msgstr "> 注意:这里的 “gas” 一词指的是 Sierra gas,而不是 L1 的 gas" #: src/ch00/testing/contract-testing.md:142 @@ -1694,16 +3244,9 @@ msgid "Now, let's move on to the testing process:" msgstr "现在,让我们进入测试过程:" #: src/ch00/testing/contract-testing.md:143 -#, fuzzy -msgid "Use the `deploy` function logic to declare and deploy your contract." -msgstr "" -"- 使用 `deploy` 函数的逻辑来声明和部署您的合约。\n" -"- 使用 `assert` 来验证合约在给定的上下文中的行为是否符合预期。" - -#: src/ch00/testing/contract-testing.md:144 -#, fuzzy msgid "" -"Use `assert` to verify that the contract behaves as expected in the given " +"- Use the `deploy` function logic to declare and deploy your contract.\n" +"- Use `assert` to verify that the contract behaves as expected in the given " "context." msgstr "" "- 使用 `deploy` 函数的逻辑来声明和部署您的合约。\n" @@ -1711,33 +3254,25 @@ msgstr "" #: src/ch00/testing/contract-testing.md:146 msgid "" -"To make testing more convenient, the `testing` module of the corelib " -"provides some helpful functions:" +"To make testing more convenient, the `testing` module of the corelib provides " +"some helpful functions:" msgstr "为了使测试更加方便,corelib 的 `testing` 模块提供了一些有用的函数:" #: src/ch00/testing/contract-testing.md:147 -msgid "`set_caller_address(address: ContractAddress)`" -msgstr "" - -#: src/ch00/testing/contract-testing.md:148 -msgid "`set_contract_address(address: ContractAddress)`" -msgstr "" - -#: src/ch00/testing/contract-testing.md:149 -msgid "`set_block_number(block_number: u64)`" -msgstr "" - -#: src/ch00/testing/contract-testing.md:150 -msgid "`set_block_timestamp(block_timestamp: u64)`" -msgstr "" - -#: src/ch00/testing/contract-testing.md:151 -msgid "`set_account_contract_address(address: ContractAddress)`" -msgstr "" - -#: src/ch00/testing/contract-testing.md:152 -msgid "`set_max_fee(fee: u128)`" +msgid "" +"- `set_caller_address(address: ContractAddress)`\n" +"- `set_contract_address(address: ContractAddress)`\n" +"- `set_block_number(block_number: u64)`\n" +"- `set_block_timestamp(block_timestamp: u64)`\n" +"- `set_account_contract_address(address: ContractAddress)`\n" +"- `set_max_fee(fee: u128)`" msgstr "" +"- `set_caller_address(address: ContractAddress)`\n" +"- `set_contract_address(address: ContractAddress)`\n" +"- `set_block_number(block_number: u64)`\n" +"- `set_block_timestamp(block_timestamp: u64)`\n" +"- `set_account_contract_address(address: ContractAddress)`\n" +"- `set_max_fee(fee: u128)`" #: src/ch00/testing/contract-testing.md:154 msgid "" @@ -1747,56 +3282,50 @@ msgstr "" "你可能还需要 corelib 中的 `info` 模块,它允许你访问有关当前交易上下文的信息:" #: src/ch00/testing/contract-testing.md:155 -msgid "`get_caller_address() -> ContractAddress`" -msgstr "" - -#: src/ch00/testing/contract-testing.md:156 -msgid "`get_contract_address() -> ContractAddress`" -msgstr "" - -#: src/ch00/testing/contract-testing.md:157 -msgid "`get_block_info() -> Box`" -msgstr "" - -#: src/ch00/testing/contract-testing.md:158 -msgid "`get_tx_info() -> Box`" -msgstr "" - -#: src/ch00/testing/contract-testing.md:159 -msgid "`get_block_timestamp() -> u64`" -msgstr "" - -#: src/ch00/testing/contract-testing.md:160 -msgid "`get_block_number() -> u64`" +msgid "" +"- `get_caller_address() -> ContractAddress`\n" +"- `get_contract_address() -> ContractAddress`\n" +"- `get_block_info() -> Box`\n" +"- `get_tx_info() -> Box`\n" +"- `get_block_timestamp() -> u64`\n" +"- `get_block_number() -> u64`" msgstr "" +"- `get_caller_address() -> ContractAddress`\n" +"- `get_contract_address() -> ContractAddress`\n" +"- `get_block_info() -> Box`\n" +"- `get_tx_info() -> Box`\n" +"- `get_block_timestamp() -> u64`\n" +"- `get_block_number() -> u64`" #: src/ch00/testing/contract-testing.md:163 -#, fuzzy msgid "" "You can found the full list of functions in the [Starknet Corelib repo]" -"(https://github.com/starkware-libs/cairo/tree/main/corelib/src/starknet). " +"(https://github.com/starkware-libs/cairo/tree/main/corelib/src/starknet).\n" "You can also find a detailled explaination of testing in cairo in the [Cairo " "book - Chapter 8](https://book.cairo-lang.org/ch08-01-how-to-write-tests." "html)." msgstr "" -"你可以在 [Starknet Corelib 仓库](https://github.com/starkware-libs/cairo/" -"tree/main/corelib/src/starknet) 中找到完整的函数列表。\n" +"你可以在 [Starknet Corelib 仓库](https://github.com/starkware-libs/cairo/tree/" +"main/corelib/src/starknet) 中找到完整的函数列表。\n" "你还可以在 [Cairo book 第8章](https://book.cairo-lang.org/ch08-01-how-to-" "write-tests.html) 中找到有关在 cairo 中进行测试的详细说明。" #: src/ch00/testing/contract-testing.md:166 -#, fuzzy -msgid "Starknet Foundry" +msgid "## Starknet Foundry" msgstr "## Starknet Foundry" +#: src/ch00/testing/contract-testing.md:168 +msgid "" +msgstr "" + #: src/ch00/testing/contract-testing.md:170 msgid "" "Starknet Foundry is a powerful toolkit for developing smart contracts on " "Starknet. It offers support for testing Starknet smart contracts on top of " "`scarb` with the `Forge` tool." msgstr "" -"Starknet Foundry是在Starknet上开发智能合约的强大工具包。它提供了对使用" -"`Forge` 工具在 `scarb` 上测试Starknet智能合约的支持。" +"Starknet Foundry是在Starknet上开发智能合约的强大工具包。它提供了对使用`Forge` " +"工具在 `scarb` 上测试Starknet智能合约的支持。" #: src/ch00/testing/contract-testing.md:172 msgid "" @@ -1806,8 +3335,8 @@ msgid "" "Starknet Foundry and incorporating it into your projects." msgstr "" "使用 `snforge` 进行测试与我们刚刚描述的过程类似,但更简化。此外,还有其他功能" -"正在开发中,包括作弊码或并行测试执行。我们强烈推荐探索Starknet Foundry并将其" -"纳入您你的项目中。" +"正在开发中,包括作弊码或并行测试执行。我们强烈推荐探索Starknet Foundry并将其纳" +"入您你的项目中。" #: src/ch00/testing/contract-testing.md:174 msgid "" @@ -1820,8 +3349,7 @@ msgstr "" "html)。" #: src/ch00/cairo_cheatsheet/cairo_cheatsheet.md:1 -#, fuzzy -msgid "Cairo Cheatsheet" +msgid "# Cairo Cheatsheet" msgstr "# Cairo 备忘单" #: src/ch00/cairo_cheatsheet/cairo_cheatsheet.md:3 @@ -1830,17 +3358,23 @@ msgid "" "constructs." msgstr "本章旨在为最常见的Cairo结构提供快速参考。" +#: src/ch00/cairo_cheatsheet/cairo_cheatsheet.md:5 +#: src/ch00/cairo_cheatsheet/mapping.md:58 +#: src/ch00/cairo_cheatsheet/arrays.md:42 src/ch00/cairo_cheatsheet/match.md:59 +#: src/ch00/cairo_cheatsheet/struct.md:15 +msgid "
Last change: 2023-10-31
" +msgstr "
Last change: 2023-10-31
" + #: src/ch00/cairo_cheatsheet/felt.md:1 -#, fuzzy -msgid "Felt252" +msgid "# Felt252" msgstr "# Felt252" #: src/ch00/cairo_cheatsheet/felt.md:3 -#, fuzzy msgid "" "Felt252 is a fundamental data type in Cairo from which all other data types " -"are derived. Felt252 can also be used to store short-string representations " -"with a maximum length of 31 characters." +"are derived.\n" +"Felt252 can also be used to store short-string representations with a maximum " +"length of 31 characters." msgstr "" "Felt252是Cairo中的基本数据类型,所有其他数据类型都派生自它。\n" "Felt252也可以用于存储最多31个字符长度的短字符串表示。" @@ -1850,175 +3384,608 @@ msgstr "" msgid "For example:" msgstr "例如:" +#: src/ch00/cairo_cheatsheet/felt.md:8 +msgid "" +"```rust\n" +" let felt: felt252 = 100;\n" +" let felt_as_str = 'Hello Starknet!';\n" +"\n" +" let felt = felt + felt_as_str;\n" +"```" +msgstr "" +"```rust\n" +" let felt: felt252 = 100;\n" +" let felt_as_str = ‘Hello Starknet!’;\n" +"\n" +" let felt = felt + felt_as_str;\n" +"```" + #: src/ch00/cairo_cheatsheet/mapping.md:1 -#, fuzzy -msgid "Mapping" -msgstr "映射" +msgid "# Mapping" +msgstr "# Mapping" #: src/ch00/cairo_cheatsheet/mapping.md:3 -#, fuzzy msgid "" -"The `LegacyMap` type can be used to represent a collection of key-value." +"The ```LegacyMap``` type can be used to represent a collection of key-value." msgstr "`LegacyMap` 类型可以用于表示键值对的集合。" -#: src/ch00/cairo_cheatsheet/mapping.md:51 +#: src/ch00/cairo_cheatsheet/mapping.md:5 msgid "" -"// for a 2D mapping its important to take note of the amount of brackets " -"being used.\n" +"```rust\n" +"use starknet::ContractAddress;\n" +"\n" +"#[starknet::interface]\n" +"trait IMappingExample {\n" +" fn register_user(ref self: TContractState, student_add: ContractAddress, " +"studentName: felt252);\n" +" fn record_student_score(\n" +" ref self: TContractState, student_add: ContractAddress, subject: " +"felt252, score: u16\n" +" );\n" +" fn view_student_name(self: @TContractState, student_add: ContractAddress) " +"-> felt252;\n" +" fn view_student_score(\n" +" self: @TContractState, student_add: ContractAddress, subject: " +"felt252\n" +" ) -> u16;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod MappingContract {\n" +" use starknet::ContractAddress;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" students_name: LegacyMap::,\n" +" students_result_record: LegacyMap::<(ContractAddress, felt252), " +"u16>,\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl External of super::IMappingExample {\n" +" fn register_user(\n" +" ref self: ContractState, student_add: ContractAddress, " +"studentName: felt252\n" +" ) {\n" +" self.students_name.write(student_add, studentName);\n" +" }\n" +"\n" +" fn record_student_score(\n" +" ref self: ContractState, student_add: ContractAddress, subject: " +"felt252, score: u16\n" +" ) {\n" +" self.students_result_record.write((student_add, subject), " +"score);\n" +" }\n" +"\n" +" fn view_student_name(self: @ContractState, student_add: " +"ContractAddress) -> felt252 {\n" +" self.students_name.read(student_add)\n" +" }\n" +"\n" +" fn view_student_score(\n" +" self: @ContractState, student_add: ContractAddress, subject: " +"felt252\n" +" ) -> u16 {\n" +" // for a 2D mapping its important to take note of the amount of " +"brackets being used.\n" +" self.students_result_record.read((student_add, subject))\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"use starknet::ContractAddress;\n" +"\n" +"#[starknet::interface]\n" +"trait IMappingExample {\n" +" fn register_user(ref self: TContractState, student_add: ContractAddress, " +"studentName: felt252);\n" +" fn record_student_score(\n" +" ref self: TContractState, student_add: ContractAddress, subject: " +"felt252, score: u16\n" +" );\n" +" fn view_student_name(self: @TContractState, student_add: ContractAddress) " +"-> felt252;\n" +" fn view_student_score(\n" +" self: @TContractState, student_add: ContractAddress, subject: " +"felt252\n" +" ) -> u16;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod MappingContract {\n" +" use starknet::ContractAddress;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" students_name: LegacyMap::,\n" +" students_result_record: LegacyMap::<(ContractAddress, felt252), " +"u16>,\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl External of super::IMappingExample {\n" +" fn register_user(\n" +" ref self: ContractState, student_add: ContractAddress, " +"studentName: felt252\n" +" ) {\n" +" self.students_name.write(student_add, studentName);\n" +" }\n" +"\n" +" fn record_student_score(\n" +" ref self: ContractState, student_add: ContractAddress, subject: " +"felt252, score: u16\n" +" ) {\n" +" self.students_result_record.write((student_add, subject), " +"score);\n" +" }\n" +"\n" +" fn view_student_name(self: @ContractState, student_add: " +"ContractAddress) -> felt252 {\n" +" self.students_name.read(student_add)\n" +" }\n" +"\n" +" fn view_student_score(\n" +" self: @ContractState, student_add: ContractAddress, subject: " +"felt252\n" +" ) -> u16 {\n" +" // 对于二维映射,重要的是注意使用的括号数量。\n" +" self.students_result_record.read((student_add, subject))\n" +" }\n" +" }\n" +"}\n" +"```" + +#: src/ch00/cairo_cheatsheet/arrays.md:1 +msgid "# Arrays" +msgstr "# 数组" #: src/ch00/cairo_cheatsheet/arrays.md:3 -#, fuzzy msgid "" -"Arrays are collections of elements of the same type. The possible operations " -"on arrays are defined with the `array::ArrayTrait` of the corelib:" +"Arrays are collections of elements of the same type.\n" +"The possible operations on arrays are defined with the `array::ArrayTrait` of " +"the corelib:" msgstr "" "数组是相同类型元素的集合。\n" "可以使用 corelib 的 `array::ArrayTrait` 来定义可能的数组操作:" -#: src/ch00/cairo_cheatsheet/arrays.md:37 -msgid "// Returns true if an array is empty, then false if it isn't.\n" +#: src/ch00/cairo_cheatsheet/arrays.md:6 +msgid "" +"```rust\n" +"trait ArrayTrait {\n" +" fn new() -> Array;\n" +" fn append(ref self: Array, value: T);\n" +" fn pop_front(ref self: Array) -> Option nopanic;\n" +" fn pop_front_consume(self: Array) -> Option<(Array, T)> nopanic;\n" +" fn get(self: @Array, index: usize) -> Option>;\n" +" fn at(self: @Array, index: usize) -> @T;\n" +" fn len(self: @Array) -> usize;\n" +" fn is_empty(self: @Array) -> bool;\n" +" fn span(self: @Array) -> Span;\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"trait ArrayTrait {\n" +" fn new() -> Array;\n" +" fn append(ref self: Array, value: T);\n" +" fn pop_front(ref self: Array) -> Option nopanic;\n" +" fn pop_front_consume(self: Array) -> Option<(Array, T)> nopanic;\n" +" fn get(self: @Array, index: usize) -> Option>;\n" +" fn at(self: @Array, index: usize) -> @T;\n" +" fn len(self: @Array) -> usize;\n" +" fn is_empty(self: @Array) -> bool;\n" +" fn span(self: @Array) -> Span;\n" +"}\n" +"```" + +#: src/ch00/cairo_cheatsheet/arrays.md:22 +msgid "" +"```rust\n" +"fn array() -> bool {\n" +" let mut arr = ArrayTrait::::new();\n" +" arr.append(10);\n" +" arr.append(20);\n" +" arr.append(30);\n" +"\n" +" assert(arr.len() == 3, 'array length should be 3');\n" +"\n" +" let first_value = arr.pop_front().unwrap();\n" +" assert(first_value == 10, 'first value should match');\n" +"\n" +" let second_value = *arr.at(0);\n" +" assert(second_value == 20, 'second value should match');\n" +"\n" +" // Returns true if an array is empty, then false if it isn't.\n" +" arr.is_empty()\n" +"}\n" +"```" msgstr "" +"```rust\n" +"fn array() -> bool {\n" +" let mut arr = ArrayTrait::::new();\n" +" arr.append(10);\n" +" arr.append(20);\n" +" arr.append(30);\n" +"\n" +" assert(arr.len() == 3, ‘array length should be 3’);\n" +"\n" +" let first_value = arr.pop_front().unwrap();\n" +" assert(first_value == 10, ‘first value should match’);\n" +"\n" +" let second_value = *arr.at(0);\n" +" assert(second_value == 20, ‘second value should match’);\n" +"\n" +" // 如果数组为空,返回 true;如果不为空,返回 false。\n" +" arr.is_empty()\n" +"}\n" +"```" + +#: src/ch00/cairo_cheatsheet/loop.md:1 +msgid "# Loop" +msgstr "# 循环" #: src/ch00/cairo_cheatsheet/loop.md:3 -#, fuzzy msgid "" "A loop specifies a block of code that will run repetitively until a halting " -"condition is encountered. For example:" +"condition is encountered.\n" +"For example:" msgstr "" "循环指定一个代码块,该代码块将重复运行,直到遇到停止条件。\n" "例如:" -#: src/ch00/cairo_cheatsheet/loop.md:9 -msgid "// Same as ~ while (i < 10) arr.append(i++);\n" +#: src/ch00/cairo_cheatsheet/loop.md:6 +msgid "" +"```rust\n" +" let mut arr = ArrayTrait::new();\n" +"\n" +" // Same as ~ while (i < 10) arr.append(i++);\n" +" let mut i: u32 = 0;\n" +" let limit = 10;\n" +" loop {\n" +" if i == limit {\n" +" break;\n" +" };\n" +"\n" +" arr.append(i);\n" +"\n" +" i += 1;\n" +" };\n" +"```" msgstr "" +"```rust\n" +" let mut arr = ArrayTrait::new();\n" +"\n" +" // 与 ~ while (i < 10) arr.append(i++); 相同\n" +" let mut i: u32 = 0;\n" +" let limit = 10;\n" +" loop {\n" +" if i == limit {\n" +" break;\n" +" };\n" +"\n" +" arr.append(i);\n" +"\n" +" i += 1;\n" +" };\n" +"```" + +#: src/ch00/cairo_cheatsheet/match.md:1 +msgid "# Match" +msgstr "# 分支" #: src/ch00/cairo_cheatsheet/match.md:3 -#, fuzzy msgid "" "The \"match\" expression in Cairo allows us to control the flow of our code " "by comparing a felt data type or an enum against various patterns and then " -"running specific code based on the pattern that matches. For example:" +"running specific code based on the pattern that matches.\n" +"For example:" msgstr "" "在 Cairo 中,”match” 表达式允许我们通过将 felt 数据类型或枚举与各种模式进行比" "较,然后根据匹配的模式运行特定的代码来控制代码的流程。例如:" +#: src/ch00/cairo_cheatsheet/match.md:6 +msgid "" +"```rust\n" +"#[derive(Drop, Serde)]\n" +"enum Colour {\n" +" Red,\n" +" Blue,\n" +" Green,\n" +" Orange,\n" +" Black\n" +"}\n" +"\n" +"#[derive(Drop, Serde)]\n" +"enum Coin {\n" +" Penny,\n" +" Nickel,\n" +" Dime,\n" +" Quarter,\n" +"}\n" +"\n" +"fn value_in_cents(coin: Coin) -> felt252 {\n" +" match coin {\n" +" Coin::Penny => 1,\n" +" Coin::Nickel => 5,\n" +" Coin::Dime => 10,\n" +" Coin::Quarter => 25,\n" +" }\n" +"}\n" +"\n" +"fn specified_colour(colour: Colour) -> felt252 {\n" +" let mut response: felt252 = '';\n" +"\n" +" match colour {\n" +" Colour::Red => { response = 'You passed in Red'; },\n" +" Colour::Blue => { response = 'You passed in Blue'; },\n" +" Colour::Green => { response = 'You passed in Green'; },\n" +" Colour::Orange => { response = 'You passed in Orange'; },\n" +" Colour::Black => { response = 'You passed in Black'; },\n" +" };\n" +"\n" +" response\n" +"}\n" +"\n" +"fn quiz(num: felt252) -> felt252 {\n" +" let mut response: felt252 = '';\n" +"\n" +" match num {\n" +" 0 => { response = 'You failed' },\n" +" _ => { response = 'You Passed' },\n" +" };\n" +"\n" +" response\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"#[derive(Drop, Serde)]\n" +"enum Colour {\n" +" Red,\n" +" Blue,\n" +" Green,\n" +" Orange,\n" +" Black\n" +"}\n" +"\n" +"#[derive(Drop, Serde)]\n" +"enum Coin {\n" +" Penny,\n" +" Nickel,\n" +" Dime,\n" +" Quarter,\n" +"}\n" +"\n" +"fn value_in_cents(coin: Coin) -> felt252 {\n" +" match coin {\n" +" Coin::Penny => 1,\n" +" Coin::Nickel => 5,\n" +" Coin::Dime => 10,\n" +" Coin::Quarter => 25,\n" +" }\n" +"}\n" +"\n" +"fn specified_colour(colour: Colour) -> felt252 {\n" +" let mut response: felt252 = ‘’;\n" +"\n" +" match colour {\n" +" Colour::Red => { response = ‘You passed in Red’; },\n" +" Colour::Blue => { response = ‘You passed in Blue’; },\n" +" Colour::Green => { response = ‘You passed in Green’; },\n" +" Colour::Orange => { response = ‘You passed in Orange’; },\n" +" Colour::Black => { response = ‘You passed in Black’; },\n" +" };\n" +"\n" +" response\n" +"}\n" +"\n" +"fn quiz(num: felt252) -> felt252 {\n" +" let mut response: felt252 = ‘’;\n" +"\n" +" match num {\n" +" 0 => { response = ‘You failed’ },\n" +" _ => { response = ‘You Passed’ },\n" +" };\n" +"\n" +" response\n" +"}\n" +"```" + +#: src/ch00/cairo_cheatsheet/tuples.md:1 +msgid "# Tuples" +msgstr "# 元组" + #: src/ch00/cairo_cheatsheet/tuples.md:3 -#, fuzzy msgid "" "Tuples is a data type to group a fixed number of items of potentially " "different types into a single compound structure. Unlike arrays, tuples have " "a set length and can contain elements of varying types. Once a tuple is " -"created, its size cannot change. For example:" +"created, its size cannot change.\n" +"For example:" msgstr "" -"元组是一种数据类型,用于将固定数量的不同类型的项组合成一个单一的复合结构。与" -"数组不同,元组具有固定的长度,并且可以包含不同类型的元素。一旦创建了元组,其" -"大小就无法更改。\n" +"元组是一种数据类型,用于将固定数量的不同类型的项组合成一个单一的复合结构。与数" +"组不同,元组具有固定的长度,并且可以包含不同类型的元素。一旦创建了元组,其大小" +"就无法更改。\n" "例如:" -#: src/ch00/cairo_cheatsheet/tuples.md:7 -msgid "\"0x000\"" -msgstr "" - -#: src/ch00/cairo_cheatsheet/tuples.md:11 -msgid "// Create tuple\n" +#: src/ch00/cairo_cheatsheet/tuples.md:6 +msgid "" +"```rust\n" +" let address = \"0x000\";\n" +" let age = 20;\n" +" let active = true;\n" +"\n" +" // Create tuple\n" +" let user_tuple = (address, age, active);\n" +"\n" +" // Access tuple\n" +" let (address, age, active) = stored_tuple;\n" +"```" msgstr "" +"```rust\n" +" let address = “0x000”;\n" +" let age = 20;\n" +" let active = true;\n" +"\n" +" // Create tuple\n" +" let user_tuple = (address, age, active);\n" +"\n" +" // Access tuple\n" +" let (address, age, active) = stored_tuple;\n" +"```" -#: src/ch00/cairo_cheatsheet/tuples.md:14 -msgid "// Access tuple\n" -msgstr "" +#: src/ch00/cairo_cheatsheet/struct.md:1 +msgid "# Struct" +msgstr "# 结构体" #: src/ch00/cairo_cheatsheet/struct.md:3 -#, fuzzy msgid "" "A struct is a data type similar to tuple. Like tuples they can be used to " -"hold data of different types. For example:" +"hold data of different types.\n" +"For example:" msgstr "" "结构体是一种类似于元组的数据类型。与元组类似,它们可以用于保存不同类型的数" "据。\n" "例如:" -#: src/ch00/cairo_cheatsheet/struct.md:7 +#: src/ch00/cairo_cheatsheet/struct.md:6 msgid "" +"```rust\n" "// With Store, you can store Data's structs in the storage part of " "contracts.\n" +"#[derive(Drop, starknet::Store)]\n" +"struct Data {\n" +" address: starknet::ContractAddress,\n" +" age: u8\n" +"}\n" +"```" msgstr "" +"```rust\n" +"// 使用 Store,您可以将 Data 结构体存储在合约的存储部分。\n" +"#[derive(Drop, starknet::Store)]\n" +"struct Data {\n" +" address: starknet::ContractAddress,\n" +" age: u8\n" +"}\n" +"```" + +#: src/ch00/cairo_cheatsheet/type_casting.md:1 +msgid "# Type casting" +msgstr "# 类型转换" #: src/ch00/cairo_cheatsheet/type_casting.md:3 -#, fuzzy msgid "" "Cairo supports the conversion from one scalar types to another by using the " -"into and try_into methods. `traits::Into` is used for conversion from a " -"smaller data type to a larger data type, while `traits::TryInto` is used " -"when converting from a larger to a smaller type that might not fit. For " -"example:" +"into and try_into methods.\n" +"`traits::Into` is used for conversion from a smaller data type to a larger " +"data type, while `traits::TryInto` is used when converting from a larger to a " +"smaller type that might not fit. \n" +"For example:" msgstr "" "Cairo支持使用into和try_into方法将一个标量类型转换为另一个类型。\n" -"`traits::Into` 用于从较小的数据类型转换为较大的数据类型,而 `traits::" -"TryInto` 用于从较大的数据类型转换为较小的数据类型,可能会发生溢出的情况。\n" +"`traits::Into` 用于从较小的数据类型转换为较大的数据类型,而 `traits::TryInto` " +"用于从较大的数据类型转换为较小的数据类型,可能会发生溢出的情况。\n" "例如:" -#: src/ch00/cairo_cheatsheet/type_casting.md:11 +#: src/ch00/cairo_cheatsheet/type_casting.md:7 msgid "" -"// Since a u32 might not fit in a u8 and a u16, we need to use try_into,\n" +"```rust\n" +" let a_number: u32 = 15;\n" +" let my_felt252 = 15;\n" +"\n" +" // Since a u32 might not fit in a u8 and a u16, we need to use try_into,\n" " // then unwrap the Option type thats returned.\n" -msgstr "" - -#: src/ch00/cairo_cheatsheet/type_casting.md:16 -msgid "" -"// since new_u32 is the of the same type (u32) as rand_number, we can " +" let new_u8: u8 = a_number.try_into().unwrap();\n" +" let new_u16: u16 = a_number.try_into().unwrap();\n" +"\n" +" // since new_u32 is the of the same type (u32) as rand_number, we can " "directly assign them,\n" " // or use the .into() method.\n" -msgstr "" - -#: src/ch00/cairo_cheatsheet/type_casting.md:20 -msgid "" -"// When typecasting from a smaller size to an equal or larger size we use " +" let new_u32: u32 = a_number;\n" +"\n" +" // When typecasting from a smaller size to an equal or larger size we use " "the .into() method.\n" " // Note: u64 and u128 are larger than u32, so a u32 type will always fit " "into them.\n" +" let new_u64: u64 = a_number.into();\n" +" let new_u128: u128 = a_number.into();\n" +"\n" +" // Since a felt252 is smaller than a u256, we can use the into() method\n" +" let new_u256: u256 = my_felt252.into();\n" +" let new_felt252: felt252 = new_u16.into();\n" +"\n" +" //note a usize is smaller than a felt so we use the try_into\n" +" let new_usize: usize = my_felt252.try_into().unwrap();\n" +"```" msgstr "" +"```rust\n" +" let a_number: u32 = 15;\n" +" let my_felt252 = 15;\n" +"\n" +" // 由于 u32 可能不匹配 u8 和 u16,我们需要使用 try_into\n" +" // 然后解包返回的 Option 类型。\n" +" let new_u8: u8 = a_number.try_into().unwrap();\n" +" let new_u16: u16 = a_number.try_into().unwrap();\n" +"\n" +" // 由于 new_u32 的类型(u32)与 a_number 相同,我们可以直接赋值\n" +" // 或使用 .into() 方法\n" +" let new_u32: u32 = a_number;\n" +"\n" +" // 当从较小的大小类型强制转换为相等或较大的大小类型时,我们使用 .into() 方" +"法\n" +" // 注意:u64 和 u128 大于 u32,所以 u32 类型将始终适合其中\n" +" let new_u64: u64 = a_number.into();\n" +" let new_u128: u128 = a_number.into();\n" +"\n" +" // 由于 felt252 比 u256 小,我们可以使用 into() 方法\n" +" let new_u256: u256 = my_felt252.into();\n" +" let new_felt252: felt252 = new_u16.into();\n" +"\n" +" //注意,usize 比 felt252 小,因此我们使用 try_into\n" +" let new_usize: usize = my_felt252.try_into().unwrap();\n" +"```" -#: src/ch00/cairo_cheatsheet/type_casting.md:25 -msgid "" -"// Since a felt252 is smaller than a u256, we can use the into() method\n" -msgstr "" - -#: src/ch00/cairo_cheatsheet/type_casting.md:29 -msgid "//note a usize is smaller than a felt so we use the try_into\n" -msgstr "" +#: src/ch01/upgradeable_contract.md:1 +msgid "# Upgradeable Contract" +msgstr "# 可升级合约" #: src/ch01/upgradeable_contract.md:3 -#, fuzzy msgid "" "In Starknet, contracts are divided into two parts: contract classes and " -"contract instances. This division follows a similar concept used in object-" -"oriented programming languages, where we distinguish between the definition " -"and implementation of objects." +"contract\n" +"instances. This division follows a similar concept used in object-oriented\n" +"programming languages, where we distinguish between the definition and " +"implementation\n" +"of objects." msgstr "" "在Starknet中,合约分为两个部分:合约类和合约实例。\n" "这种划分遵循了面向对象编程语言中的类和实例的概念。\n" "这样,我们区分了对象的定义和实现。" #: src/ch01/upgradeable_contract.md:8 -#, fuzzy msgid "" "A contract class is the definition of a contract: it specifies how the " -"contract behaves. It contains essential information like the Cairo byte " -"code, hint information, entry point names, and everything that defines its " -"semantics unambiguously." +"contract\n" +"behaves. It contains essential information like the Cairo byte code, hint\n" +"information, entry point names, and everything that defines its semantics\n" +"unambiguously." msgstr "" "合约类是合约的定义:它指定了合约的行为方式。\n" "合约类包含了关键信息,如Cairo字节码、提示信息、入口点名称等,\n" "以及一切明确定义合约类语义的内容。" #: src/ch01/upgradeable_contract.md:13 -#, fuzzy msgid "" "To identify different contract classes, Starknet assigns a unique identifier " -"to each class: the class hash. A contract instance is a deployed contract " -"that corresponds to a specific contract class. Think of it as an instance of " -"an object in languages like Java." +"to each\n" +"class: the class hash. A contract instance is a deployed contract that " +"corresponds to\n" +"a specific contract class. Think of it as an instance of an object in " +"languages like\n" +"Java." msgstr "" "为了识别不同的合约类,Starknet为每个类分配一个唯一的标识符:类哈希。\n" "合约实例是对应于特定合约类的已部署合约。\n" @@ -2030,8 +3997,8 @@ msgid "" "name in an object-oriented programming language. A contract instance is a " "deployed contract corresponding to a class." msgstr "" -"每个类由其类哈希值标识,类似于面向对象编程语言中的类名。合约实例是对应于某个" -"类的已部署合约。" +"每个类由其类哈希值标识,类似于面向对象编程语言中的类名。合约实例是对应于某个类" +"的已部署合约。" #: src/ch01/upgradeable_contract.md:20 msgid "" @@ -2042,26 +4009,117 @@ msgid "" "the data stored in the contract will remain the same." msgstr "" "当调用`replace_class_syscall`函数,你可以将已部署的合约升级到更新的版本。通过" -"使用这个函数,你可以更新与已部署合约相关联的类哈希,从而有效地升级合约的实" -"现。然而,这不会修改合约中的存储,因此合约中存储的所有数据将保持不变。" +"使用这个函数,你可以更新与已部署合约相关联的类哈希,从而有效地升级合约的实现。" +"然而,这不会修改合约中的存储,因此合约中存储的所有数据将保持不变。" #: src/ch01/upgradeable_contract.md:22 -#, fuzzy msgid "" "To illustrate this concept, let's consider an example with two contracts: " -"`UpgradeableContract_V0`, and `UpgradeableContract_V1`. Start by deploying " -"`UpgradeableContract_V0` as the initial version. Next, send a transaction " -"that invokes the `upgrade` function, with the class hash of " -"`UpgradeableContract_V1` as parameter to upgrade the class hash of the " +"`UpgradeableContract_V0`, and `UpgradeableContract_V1`.\n" +"Start by deploying `UpgradeableContract_V0` as the initial version. Next, " +"send a transaction that invokes the `upgrade` function, with the class hash " +"of `UpgradeableContract_V1` as parameter to upgrade the class hash of the " "deployed contract to the `UpgradeableContract_V1` one. Then, call the " -"`version` method on the contract to see that the contract was upgraded to " -"the V1 version." +"`version` method on the contract to see that the contract was upgraded to the " +"V1 version." msgstr "" "为了说明这个概念,让我们以两个合约为例:`UpgradeableContract_V0`和" "`UpgradeableContract_V1`。\n" "首先,部署`UpgradeableContract_V0`作为初始版本。接下来,发送一个调用`upgrade`" -"函数的交易,将部署合约的类哈希升级为`UpgradeableContract_V1`的类哈希。然后," -"调用合约上的`version`方法,查看合约是否已升级到V1版本。" +"函数的交易,将部署合约的类哈希升级为`UpgradeableContract_V1`的类哈希。然后,调" +"用合约上的`version`方法,查看合约是否已升级到V1版本。" + +#: src/ch01/upgradeable_contract.md:25 +msgid "" +"```rust\n" +"use starknet::class_hash::ClassHash;\n" +"\n" +"#[starknet::interface]\n" +"trait IUpgradeableContract {\n" +" fn upgrade(ref self: TContractState, impl_hash: ClassHash);\n" +" fn version(self: @TContractState) -> u8;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod UpgradeableContract_V0 {\n" +" use starknet::class_hash::ClassHash;\n" +" use starknet::SyscallResultTrait;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +"\n" +" #[event]\n" +" #[derive(Drop, starknet::Event)]\n" +" enum Event {\n" +" Upgraded: Upgraded\n" +" }\n" +"\n" +" #[derive(Drop, starknet::Event)]\n" +" struct Upgraded {\n" +" implementation: ClassHash\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl UpgradeableContract of super::IUpgradeableContract {\n" +" fn upgrade(ref self: ContractState, impl_hash: ClassHash) {\n" +" assert(!impl_hash.is_zero(), 'Class hash cannot be zero');\n" +" starknet::replace_class_syscall(impl_hash).unwrap_syscall();\n" +" self.emit(Event::Upgraded(Upgraded { implementation: " +"impl_hash }))\n" +" }\n" +"\n" +" fn version(self: @ContractState) -> u8 {\n" +" 0\n" +" }\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"use starknet::class_hash::ClassHash;\n" +"\n" +"#[starknet::interface]\n" +"trait IUpgradeableContract {\n" +" fn upgrade(ref self: TContractState, impl_hash: ClassHash);\n" +" fn version(self: @TContractState) -> u8;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod UpgradeableContract_V0 {\n" +" use starknet::class_hash::ClassHash;\n" +" use starknet::SyscallResultTrait;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +"\n" +" #[event]\n" +" #[derive(Drop, starknet::Event)]\n" +" enum Event {\n" +" Upgraded: Upgraded\n" +" }\n" +"\n" +" #[derive(Drop, starknet::Event)]\n" +" struct Upgraded {\n" +" implementation: ClassHash\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl UpgradeableContract of super::IUpgradeableContract {\n" +" fn upgrade(ref self: ContractState, impl_hash: ClassHash) {\n" +" assert(!impl_hash.is_zero(), 'Class hash cannot be zero');\n" +" starknet::replace_class_syscall(impl_hash).unwrap_syscall();\n" +" self.emit(Event::Upgraded(Upgraded { implementation: " +"impl_hash }))\n" +" }\n" +"\n" +" fn version(self: @ContractState) -> u8 {\n" +" 0\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch01/upgradeable_contract.md:68 msgid "" @@ -2079,6 +4137,98 @@ msgstr "" "blob/main/listings/ch01-applications/upgradeable_contract/src/" "upgradeable_contract_v0.cairo)中尝试它 。" +#: src/ch01/upgradeable_contract.md:71 +msgid "" +"```rust\n" +"use starknet::class_hash::ClassHash;\n" +"\n" +"#[starknet::interface]\n" +"trait IUpgradeableContract {\n" +" fn upgrade(ref self: TContractState, impl_hash: ClassHash);\n" +" fn version(self: @TContractState) -> u8;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod UpgradeableContract_V1 {\n" +" use starknet::class_hash::ClassHash;\n" +" use starknet::SyscallResultTrait;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +"\n" +" #[event]\n" +" #[derive(Drop, starknet::Event)]\n" +" enum Event {\n" +" Upgraded: Upgraded\n" +" }\n" +"\n" +" #[derive(Drop, starknet::Event)]\n" +" struct Upgraded {\n" +" implementation: ClassHash\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl UpgradeableContract of super::IUpgradeableContract {\n" +" fn upgrade(ref self: ContractState, impl_hash: ClassHash) {\n" +" assert(!impl_hash.is_zero(), 'Class hash cannot be zero');\n" +" starknet::replace_class_syscall(impl_hash).unwrap_syscall();\n" +" self.emit(Event::Upgraded(Upgraded { implementation: " +"impl_hash }))\n" +" }\n" +"\n" +" fn version(self: @ContractState) -> u8 {\n" +" 1\n" +" }\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"use starknet::class_hash::ClassHash;\n" +"\n" +"#[starknet::interface]\n" +"trait IUpgradeableContract {\n" +" fn upgrade(ref self: TContractState, impl_hash: ClassHash);\n" +" fn version(self: @TContractState) -> u8;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod UpgradeableContract_V1 {\n" +" use starknet::class_hash::ClassHash;\n" +" use starknet::SyscallResultTrait;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +"\n" +" #[event]\n" +" #[derive(Drop, starknet::Event)]\n" +" enum Event {\n" +" Upgraded: Upgraded\n" +" }\n" +"\n" +" #[derive(Drop, starknet::Event)]\n" +" struct Upgraded {\n" +" implementation: ClassHash\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl UpgradeableContract of super::IUpgradeableContract {\n" +" fn upgrade(ref self: ContractState, impl_hash: ClassHash) {\n" +" assert(!impl_hash.is_zero(), 'Class hash cannot be zero');\n" +" starknet::replace_class_syscall(impl_hash).unwrap_syscall();\n" +" self.emit(Event::Upgraded(Upgraded { implementation: " +"impl_hash }))\n" +" }\n" +"\n" +" fn version(self: @ContractState) -> u8 {\n" +" 1\n" +" }\n" +" }\n" +"}\n" +"```" + #: src/ch01/upgradeable_contract.md:114 msgid "" "Visit contract on [Voyager](https://goerli.voyager.online/" @@ -2096,30 +4246,25 @@ msgstr "" "upgradeable_contract_v1.cairo) 中尝试它。" #: src/ch01/simple_vault.md:1 -#, fuzzy -msgid "Simple Defi Vault" +msgid "# Simple Defi Vault" msgstr "# 简单的去中心化金融保险库" #: src/ch01/simple_vault.md:3 -#, fuzzy msgid "" "This is the Cairo adaptation of the [Solidity by example Vault](https://" -"solidity-by-example.org/defi/vault/). Here's how it works:" +"solidity-by-example.org/defi/vault/).\n" +"Here's how it works:" msgstr "" -"这是 [Solidity by example Vault](https://solidity-by-example.org/defi/" -"vault/) 的Cairo版本\n" +"这是 [Solidity by example Vault](https://solidity-by-example.org/defi/vault/) " +"的Cairo版本\n" "以下是它的工作原理:" #: src/ch01/simple_vault.md:6 msgid "" -"When a user deposits a token, the contract calculates the amount of shares " -"to mint." -msgstr "" - -#: src/ch01/simple_vault.md:8 -#, fuzzy -msgid "" -"When a user withdraws, the contract burns their shares, calculates the " +"- When a user deposits a token, the contract calculates the amount of shares " +"to mint.\n" +"\n" +"- When a user withdraws, the contract burns their shares, calculates the " "yield, and withdraw both the yield and the initial amount of token deposited." msgstr "" "- 当用户存入代笔时,合约会计算要铸造的份额数量。\n" @@ -2127,16 +4272,76 @@ msgstr "" "- 当用户取款时,合约会销毁他们的份额,计算收益,并提取存款的收益和初始代币金" "额。" -#: src/ch01/simple_vault.md:12 +#: src/ch01/simple_vault.md:10 msgid "" +"```rust\n" +"use starknet::{ContractAddress};\n" +"\n" "// In order to make contract calls within our Vault,\n" "// we need to have the interface of the remote ERC20 contract defined to " "import the Dispatcher.\n" -msgstr "" - -#: src/ch01/simple_vault.md:68 -msgid "" -"// a = amount\n" +"#[starknet::interface]\n" +"trait IERC20 {\n" +" fn name(self: @TContractState) -> felt252;\n" +" fn symbol(self: @TContractState) -> felt252;\n" +" fn decimals(self: @TContractState) -> u8;\n" +" fn total_supply(self: @TContractState) -> u256;\n" +" fn balance_of(self: @TContractState, account: ContractAddress) -> u256;\n" +" fn allowance(self: @TContractState, owner: ContractAddress, spender: " +"ContractAddress) -> u256;\n" +" fn transfer(ref self: TContractState, recipient: ContractAddress, amount: " +"u256) -> bool;\n" +" fn transfer_from(\n" +" ref self: TContractState, sender: ContractAddress, recipient: " +"ContractAddress, amount: u256\n" +" ) -> bool;\n" +" fn approve(ref self: TContractState, spender: ContractAddress, amount: " +"u256) -> bool;\n" +"}\n" +"\n" +"#[starknet::interface]\n" +"trait ISimpleVault {\n" +" fn deposit(ref self: TContractState, amount: u256);\n" +" fn withdraw(ref self: TContractState, shares: u256);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod SimpleVault {\n" +" use super::{IERC20Dispatcher, IERC20DispatcherTrait};\n" +" use starknet::{ContractAddress, get_caller_address, " +"get_contract_address};\n" +" #[storage]\n" +" struct Storage {\n" +" token: IERC20Dispatcher,\n" +" total_supply: u256,\n" +" balance_of: LegacyMap\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(ref self: ContractState, token: ContractAddress) {\n" +" self.token.write(IERC20Dispatcher { contract_address: token });\n" +" }\n" +"\n" +" #[generate_trait]\n" +" impl PrivateFunctions of PrivateFunctionsTrait {\n" +" fn _mint(ref self: ContractState, to: ContractAddress, shares: u256) " +"{\n" +" self.total_supply.write(self.total_supply.read() + shares);\n" +" self.balance_of.write(to, self.balance_of.read(to) + shares);\n" +" }\n" +"\n" +" fn _burn(ref self: ContractState, from: ContractAddress, shares: " +"u256) {\n" +" self.total_supply.write(self.total_supply.read() - shares);\n" +" self.balance_of.write(from, self.balance_of.read(from) - " +"shares);\n" +" }\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl SimpleVault of super::ISimpleVault {\n" +" fn deposit(ref self: ContractState, amount: u256) {\n" +" // a = amount\n" " // B = balance of token before deposit\n" " // T = total supply\n" " // s = shares to mint\n" @@ -2144,11 +4349,23 @@ msgid "" " // (T + s) / T = (a + B) / B \n" " //\n" " // s = aT / B\n" -msgstr "" - -#: src/ch01/simple_vault.md:92 -msgid "" -"// a = amount\n" +" let caller = get_caller_address();\n" +" let this = get_contract_address();\n" +"\n" +" let mut shares = 0;\n" +" if self.total_supply.read() == 0 {\n" +" shares = amount;\n" +" } else {\n" +" let balance = self.token.read().balance_of(this);\n" +" shares = (amount * self.total_supply.read()) / balance;\n" +" }\n" +"\n" +" PrivateFunctions::_mint(ref self, caller, shares);\n" +" self.token.read().transfer_from(caller, this, amount);\n" +" }\n" +"\n" +" fn withdraw(ref self: ContractState, shares: u256) {\n" +" // a = amount\n" " // B = balance of token before withdraw\n" " // T = total supply\n" " // s = shares to burn\n" @@ -2156,7 +4373,130 @@ msgid "" " // (T - s) / T = (B - a) / B \n" " //\n" " // a = sB / T\n" +" let caller = get_caller_address();\n" +" let this = get_contract_address();\n" +"\n" +" let balance = self.token.read().balance_of(this);\n" +" let amount = (shares * balance) / self.total_supply.read();\n" +" PrivateFunctions::_burn(ref self, caller, shares);\n" +" self.token.read().transfer(caller, amount);\n" +" }\n" +" }\n" +"}\n" +"\n" +"```" msgstr "" +"```rust\n" +"use starknet::{ContractAddress};\n" +"\n" +"// In order to make contract calls within our Vault,\n" +"// we need to have the interface of the remote ERC20 contract defined to " +"import the Dispatcher.\n" +"#[starknet::interface]\n" +"trait IERC20 {\n" +" fn name(self: @TContractState) -> felt252;\n" +" fn symbol(self: @TContractState) -> felt252;\n" +" fn decimals(self: @TContractState) -> u8;\n" +" fn total_supply(self: @TContractState) -> u256;\n" +" fn balance_of(self: @TContractState, account: ContractAddress) -> u256;\n" +" fn allowance(self: @TContractState, owner: ContractAddress, spender: " +"ContractAddress) -> u256;\n" +" fn transfer(ref self: TContractState, recipient: ContractAddress, amount: " +"u256) -> bool;\n" +" fn transfer_from(\n" +" ref self: TContractState, sender: ContractAddress, recipient: " +"ContractAddress, amount: u256\n" +" ) -> bool;\n" +" fn approve(ref self: TContractState, spender: ContractAddress, amount: " +"u256) -> bool;\n" +"}\n" +"\n" +"#[starknet::interface]\n" +"trait ISimpleVault {\n" +" fn deposit(ref self: TContractState, amount: u256);\n" +" fn withdraw(ref self: TContractState, shares: u256);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod SimpleVault {\n" +" use super::{IERC20Dispatcher, IERC20DispatcherTrait};\n" +" use starknet::{ContractAddress, get_caller_address, " +"get_contract_address};\n" +" #[storage]\n" +" struct Storage {\n" +" token: IERC20Dispatcher,\n" +" total_supply: u256,\n" +" balance_of: LegacyMap\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(ref self: ContractState, token: ContractAddress) {\n" +" self.token.write(IERC20Dispatcher { contract_address: token });\n" +" }\n" +"\n" +" #[generate_trait]\n" +" impl PrivateFunctions of PrivateFunctionsTrait {\n" +" fn _mint(ref self: ContractState, to: ContractAddress, shares: u256) " +"{\n" +" self.total_supply.write(self.total_supply.read() + shares);\n" +" self.balance_of.write(to, self.balance_of.read(to) + shares);\n" +" }\n" +"\n" +" fn _burn(ref self: ContractState, from: ContractAddress, shares: " +"u256) {\n" +" self.total_supply.write(self.total_supply.read() - shares);\n" +" self.balance_of.write(from, self.balance_of.read(from) - " +"shares);\n" +" }\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl SimpleVault of super::ISimpleVault {\n" +" fn deposit(ref self: ContractState, amount: u256) {\n" +" // a = amount\n" +" // B = balance of token before deposit\n" +" // T = total supply\n" +" // s = shares to mint\n" +" //\n" +" // (T + s) / T = (a + B) / B \n" +" //\n" +" // s = aT / B\n" +" let caller = get_caller_address();\n" +" let this = get_contract_address();\n" +"\n" +" let mut shares = 0;\n" +" if self.total_supply.read() == 0 {\n" +" shares = amount;\n" +" } else {\n" +" let balance = self.token.read().balance_of(this);\n" +" shares = (amount * self.total_supply.read()) / balance;\n" +" }\n" +"\n" +" PrivateFunctions::_mint(ref self, caller, shares);\n" +" self.token.read().transfer_from(caller, this, amount);\n" +" }\n" +"\n" +" fn withdraw(ref self: ContractState, shares: u256) {\n" +" // a = amount\n" +" // B = balance of token before withdraw\n" +" // T = total supply\n" +" // s = shares to burn\n" +" //\n" +" // (T - s) / T = (B - a) / B \n" +" //\n" +" // a = sB / T\n" +" let caller = get_caller_address();\n" +" let this = get_contract_address();\n" +"\n" +" let balance = self.token.read().balance_of(this);\n" +" let amount = (shares * balance) / self.total_supply.read();\n" +" PrivateFunctions::_burn(ref self, caller, shares);\n" +" self.token.read().transfer(caller, amount);\n" +" }\n" +" }\n" +"}\n" +"\n" +"```" #: src/ch01/simple_vault.md:113 msgid "" @@ -2168,6 +4508,10 @@ msgstr "" "com/NethermindEth/StarknetByExample/blob/main/listings/ch01-applications/" "simple_vault/src/simple_vault.cairo) 中尝试这个合约。" +#: src/ch01/erc20.md:1 +msgid "# ERC20 Token" +msgstr "# ERC20 代币" + #: src/ch01/erc20.md:3 msgid "" "Contracts that follow the [ERC20 Standard](https://eips.ethereum.org/EIPS/" @@ -2177,29 +4521,493 @@ msgstr "" "ERC20 代币。它们用于代表可互换的资产。" #: src/ch01/erc20.md:5 -msgid "" -"To create an ERC20 conctract, it must implement the following interface:" +msgid "To create an ERC20 conctract, it must implement the following interface:" msgstr "要创建 ERC20 合约,必须实现以下接口:" +#: src/ch01/erc20.md:7 +msgid "" +"```rust\n" +"#[starknet::interface]\n" +"trait IERC20 {\n" +" fn get_name(self: @TContractState) -> felt252;\n" +" fn get_symbol(self: @TContractState) -> felt252;\n" +" fn get_decimals(self: @TContractState) -> u8;\n" +" fn get_total_supply(self: @TContractState) -> felt252;\n" +" fn balance_of(self: @TContractState, account: ContractAddress) -> " +"felt252;\n" +" fn allowance(\n" +" self: @TContractState, owner: ContractAddress, spender: " +"ContractAddress\n" +" ) -> felt252;\n" +" fn transfer(ref self: TContractState, recipient: ContractAddress, amount: " +"felt252);\n" +" fn transfer_from(\n" +" ref self: TContractState,\n" +" sender: ContractAddress,\n" +" recipient: ContractAddress,\n" +" amount: felt252\n" +" );\n" +" fn approve(ref self: TContractState, spender: ContractAddress, amount: " +"felt252);\n" +" fn increase_allowance(ref self: TContractState, spender: ContractAddress, " +"added_value: felt252);\n" +" fn decrease_allowance(\n" +" ref self: TContractState, spender: ContractAddress, subtracted_value: " +"felt252\n" +" );\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait IERC20 {\n" +" fn get_name(self: @TContractState) -> felt252;\n" +" fn get_symbol(self: @TContractState) -> felt252;\n" +" fn get_decimals(self: @TContractState) -> u8;\n" +" fn get_total_supply(self: @TContractState) -> felt252;\n" +" fn balance_of(self: @TContractState, account: ContractAddress) -> " +"felt252;\n" +" fn allowance(\n" +" self: @TContractState, owner: ContractAddress, spender: " +"ContractAddress\n" +" ) -> felt252;\n" +" fn transfer(ref self: TContractState, recipient: ContractAddress, amount: " +"felt252);\n" +" fn transfer_from(\n" +" ref self: TContractState,\n" +" sender: ContractAddress,\n" +" recipient: ContractAddress,\n" +" amount: felt252\n" +" );\n" +" fn approve(ref self: TContractState, spender: ContractAddress, amount: " +"felt252);\n" +" fn increase_allowance(ref self: TContractState, spender: ContractAddress, " +"added_value: felt252);\n" +" fn decrease_allowance(\n" +" ref self: TContractState, spender: ContractAddress, subtracted_value: " +"felt252\n" +" );\n" +"}\n" +"```" + #: src/ch01/erc20.md:33 -#, fuzzy msgid "" -"In Starknet, function names should be written in _snake_case_. This is not " -"the case in Solidity, where function names are written in _camelCase_. The " -"Starknet ERC20 interface is therefore slightly different from the Solidity " -"ERC20 interface." +"In Starknet, function names should be written in *snake_case*. This is not " +"the case in Solidity, where function names are written in *camelCase*.\n" +"The Starknet ERC20 interface is therefore slightly different from the " +"Solidity ERC20 interface." msgstr "" -"在Starknet中,函数名应该使用*snake_case*(蛇形命名法)。而在Solidity中,函数" -"名使用*camelCase*(驼峰命名法)。因此,Starknet的ERC20接口与Solidity的ERC20接" -"口略有不同。" +"在Starknet中,函数名应该使用*snake_case*(蛇形命名法)。而在Solidity中,函数名" +"使用*camelCase*(驼峰命名法)。因此,Starknet的ERC20接口与Solidity的ERC20接口" +"略有不同。" #: src/ch01/erc20.md:36 msgid "Here's an implementation of the ERC20 interface in Cairo:" msgstr "以下是一个在Cairo中实现的ERC20接口的示例:" -#: src/ch01/erc20.md:207 -msgid "// What can go wrong here?\n" +#: src/ch01/erc20.md:38 +msgid "" +"```rust\n" +"#[starknet::contract]\n" +"mod erc20 {\n" +" use zeroable::Zeroable;\n" +" use starknet::get_caller_address;\n" +" use starknet::contract_address_const;\n" +" use starknet::ContractAddress;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" name: felt252,\n" +" symbol: felt252,\n" +" decimals: u8,\n" +" total_supply: felt252,\n" +" balances: LegacyMap::,\n" +" allowances: LegacyMap::<(ContractAddress, ContractAddress), " +"felt252>,\n" +" }\n" +"\n" +" #[event]\n" +" #[derive(Drop, starknet::Event)]\n" +" enum Event {\n" +" Transfer: Transfer,\n" +" Approval: Approval,\n" +" }\n" +" #[derive(Drop, starknet::Event)]\n" +" struct Transfer {\n" +" from: ContractAddress,\n" +" to: ContractAddress,\n" +" value: felt252,\n" +" }\n" +" #[derive(Drop, starknet::Event)]\n" +" struct Approval {\n" +" owner: ContractAddress,\n" +" spender: ContractAddress,\n" +" value: felt252,\n" +" }\n" +"\n" +" mod Errors {\n" +" const APPROVE_FROM_ZERO: felt252 = 'ERC20: approve from 0';\n" +" const APPROVE_TO_ZERO: felt252 = 'ERC20: approve to 0';\n" +" const TRANSFER_FROM_ZERO: felt252 = 'ERC20: transfer from 0';\n" +" const TRANSFER_TO_ZERO: felt252 = 'ERC20: transfer to 0';\n" +" const BURN_FROM_ZERO: felt252 = 'ERC20: burn from 0';\n" +" const MINT_TO_ZERO: felt252 = 'ERC20: mint to 0';\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(\n" +" ref self: ContractState,\n" +" recipient: ContractAddress,\n" +" name: felt252,\n" +" decimals: u8,\n" +" initial_supply: felt252,\n" +" symbol: felt252\n" +" ) {\n" +" self.name.write(name);\n" +" self.symbol.write(symbol);\n" +" self.decimals.write(decimals);\n" +" self.mint(recipient, initial_supply);\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl IERC20Impl of super::IERC20 {\n" +" fn get_name(self: @ContractState) -> felt252 {\n" +" self.name.read()\n" +" }\n" +"\n" +" fn get_symbol(self: @ContractState) -> felt252 {\n" +" self.symbol.read()\n" +" }\n" +"\n" +" fn get_decimals(self: @ContractState) -> u8 {\n" +" self.decimals.read()\n" +" }\n" +"\n" +" fn get_total_supply(self: @ContractState) -> felt252 {\n" +" self.total_supply.read()\n" +" }\n" +"\n" +" fn balance_of(self: @ContractState, account: ContractAddress) -> " +"felt252 {\n" +" self.balances.read(account)\n" +" }\n" +"\n" +" fn allowance(\n" +" self: @ContractState, owner: ContractAddress, spender: " +"ContractAddress\n" +" ) -> felt252 {\n" +" self.allowances.read((owner, spender))\n" +" }\n" +"\n" +" fn transfer(ref self: ContractState, recipient: ContractAddress, " +"amount: felt252) {\n" +" let sender = get_caller_address();\n" +" self._transfer(sender, recipient, amount);\n" +" }\n" +"\n" +" fn transfer_from(\n" +" ref self: ContractState,\n" +" sender: ContractAddress,\n" +" recipient: ContractAddress,\n" +" amount: felt252\n" +" ) {\n" +" let caller = get_caller_address();\n" +" self.spend_allowance(sender, caller, amount);\n" +" self._transfer(sender, recipient, amount);\n" +" }\n" +"\n" +" fn approve(ref self: ContractState, spender: ContractAddress, amount: " +"felt252) {\n" +" let caller = get_caller_address();\n" +" self.approve_helper(caller, spender, amount);\n" +" }\n" +"\n" +" fn increase_allowance(\n" +" ref self: ContractState, spender: ContractAddress, added_value: " +"felt252\n" +" ) {\n" +" let caller = get_caller_address();\n" +" self\n" +" .approve_helper(\n" +" caller, spender, self.allowances.read((caller, spender)) " +"+ added_value\n" +" );\n" +" }\n" +"\n" +" fn decrease_allowance(\n" +" ref self: ContractState, spender: ContractAddress, " +"subtracted_value: felt252\n" +" ) {\n" +" let caller = get_caller_address();\n" +" self\n" +" .approve_helper(\n" +" caller, spender, self.allowances.read((caller, spender)) " +"- subtracted_value\n" +" );\n" +" }\n" +" }\n" +"\n" +" #[generate_trait]\n" +" impl InternalImpl of InternalTrait {\n" +" fn _transfer(\n" +" ref self: ContractState,\n" +" sender: ContractAddress,\n" +" recipient: ContractAddress,\n" +" amount: felt252\n" +" ) {\n" +" assert(!sender.is_zero(), Errors::TRANSFER_FROM_ZERO);\n" +" assert(!recipient.is_zero(), Errors::TRANSFER_TO_ZERO);\n" +" self.balances.write(sender, self.balances.read(sender) - " +"amount);\n" +" self.balances.write(recipient, self.balances.read(recipient) + " +"amount);\n" +" self.emit(Transfer { from: sender, to: recipient, value: " +"amount });\n" +" }\n" +"\n" +" fn spend_allowance(\n" +" ref self: ContractState,\n" +" owner: ContractAddress,\n" +" spender: ContractAddress,\n" +" amount: felt252\n" +" ) {\n" +" let allowance = self.allowances.read((owner, spender));\n" +" self.allowances.write((owner, spender), allowance - amount);\n" +" }\n" +"\n" +" fn approve_helper(\n" +" ref self: ContractState,\n" +" owner: ContractAddress,\n" +" spender: ContractAddress,\n" +" amount: felt252\n" +" ) {\n" +" assert(!spender.is_zero(), Errors::APPROVE_TO_ZERO);\n" +" self.allowances.write((owner, spender), amount);\n" +" self.emit(Approval { owner, spender, value: amount });\n" +" }\n" +"\n" +" fn mint(ref self: ContractState, recipient: ContractAddress, amount: " +"felt252) {\n" +" assert(!recipient.is_zero(), Errors::MINT_TO_ZERO);\n" +" let supply = self.total_supply.read() + amount; // What can go " +"wrong here?\n" +" self.total_supply.write(supply);\n" +" let balance = self.balances.read(recipient) + amount;\n" +" self.balances.write(recipient, amount);\n" +" self\n" +" .emit(\n" +" Event::Transfer(\n" +" Transfer {\n" +" from: contract_address_const::<0>(), to: " +"recipient, value: amount\n" +" }\n" +" )\n" +" );\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"#[starknet::contract]\n" +"mod erc20 {\n" +" use zeroable::Zeroable;\n" +" use starknet::get_caller_address;\n" +" use starknet::contract_address_const;\n" +" use starknet::ContractAddress;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" name: felt252,\n" +" symbol: felt252,\n" +" decimals: u8,\n" +" total_supply: felt252,\n" +" balances: LegacyMap::,\n" +" allowances: LegacyMap::<(ContractAddress, ContractAddress), " +"felt252>,\n" +" }\n" +"\n" +" #[event]\n" +" #[derive(Drop, starknet::Event)]\n" +" enum Event {\n" +" Transfer: Transfer,\n" +" Approval: Approval,\n" +" }\n" +" #[derive(Drop, starknet::Event)]\n" +" struct Transfer {\n" +" from: ContractAddress,\n" +" to: ContractAddress,\n" +" value: felt252,\n" +" }\n" +" #[derive(Drop, starknet::Event)]\n" +" struct Approval {\n" +" owner: ContractAddress,\n" +" spender: ContractAddress,\n" +" value: felt252,\n" +" }\n" +"\n" +" mod Errors {\n" +" const APPROVE_FROM_ZERO: felt252 = 'ERC20: approve from 0';\n" +" const APPROVE_TO_ZERO: felt252 = 'ERC20: approve to 0';\n" +" const TRANSFER_FROM_ZERO: felt252 = 'ERC20: transfer from 0';\n" +" const TRANSFER_TO_ZERO: felt252 = 'ERC20: transfer to 0';\n" +" const BURN_FROM_ZERO: felt252 = 'ERC20: burn from 0';\n" +" const MINT_TO_ZERO: felt252 = 'ERC20: mint to 0';\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(\n" +" ref self: ContractState,\n" +" recipient: ContractAddress,\n" +" name: felt252,\n" +" decimals: u8,\n" +" initial_supply: felt252,\n" +" symbol: felt252\n" +" ) {\n" +" self.name.write(name);\n" +" self.symbol.write(symbol);\n" +" self.decimals.write(decimals);\n" +" self.mint(recipient, initial_supply);\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl IERC20Impl of super::IERC20 {\n" +" fn get_name(self: @ContractState) -> felt252 {\n" +" self.name.read()\n" +" }\n" +"\n" +" fn get_symbol(self: @ContractState) -> felt252 {\n" +" self.symbol.read()\n" +" }\n" +"\n" +" fn get_decimals(self: @ContractState) -> u8 {\n" +" self.decimals.read()\n" +" }\n" +"\n" +" fn get_total_supply(self: @ContractState) -> felt252 {\n" +" self.total_supply.read()\n" +" }\n" +"\n" +" fn balance_of(self: @ContractState, account: ContractAddress) -> " +"felt252 {\n" +" self.balances.read(account)\n" +" }\n" +"\n" +" fn allowance(\n" +" self: @ContractState, owner: ContractAddress, spender: " +"ContractAddress\n" +" ) -> felt252 {\n" +" self.allowances.read((owner, spender))\n" +" }\n" +"\n" +" fn transfer(ref self: ContractState, recipient: ContractAddress, " +"amount: felt252) {\n" +" let sender = get_caller_address();\n" +" self._transfer(sender, recipient, amount);\n" +" }\n" +"\n" +" fn transfer_from(\n" +" ref self: ContractState,\n" +" sender: ContractAddress,\n" +" recipient: ContractAddress,\n" +" amount: felt252\n" +" ) {\n" +" let caller = get_caller_address();\n" +" self.spend_allowance(sender, caller, amount);\n" +" self._transfer(sender, recipient, amount);\n" +" }\n" +"\n" +" fn approve(ref self: ContractState, spender: ContractAddress, amount: " +"felt252) {\n" +" let caller = get_caller_address();\n" +" self.approve_helper(caller, spender, amount);\n" +" }\n" +"\n" +" fn increase_allowance(\n" +" ref self: ContractState, spender: ContractAddress, added_value: " +"felt252\n" +" ) {\n" +" let caller = get_caller_address();\n" +" self\n" +" .approve_helper(\n" +" caller, spender, self.allowances.read((caller, spender)) " +"+ added_value\n" +" );\n" +" }\n" +"\n" +" fn decrease_allowance(\n" +" ref self: ContractState, spender: ContractAddress, " +"subtracted_value: felt252\n" +" ) {\n" +" let caller = get_caller_address();\n" +" self\n" +" .approve_helper(\n" +" caller, spender, self.allowances.read((caller, spender)) " +"- subtracted_value\n" +" );\n" +" }\n" +" }\n" +"\n" +" #[generate_trait]\n" +" impl InternalImpl of InternalTrait {\n" +" fn _transfer(\n" +" ref self: ContractState,\n" +" sender: ContractAddress,\n" +" recipient: ContractAddress,\n" +" amount: felt252\n" +" ) {\n" +" assert(!sender.is_zero(), Errors::TRANSFER_FROM_ZERO);\n" +" assert(!recipient.is_zero(), Errors::TRANSFER_TO_ZERO);\n" +" self.balances.write(sender, self.balances.read(sender) - " +"amount);\n" +" self.balances.write(recipient, self.balances.read(recipient) + " +"amount);\n" +" self.emit(Transfer { from: sender, to: recipient, value: " +"amount });\n" +" }\n" +"\n" +" fn spend_allowance(\n" +" ref self: ContractState,\n" +" owner: ContractAddress,\n" +" spender: ContractAddress,\n" +" amount: felt252\n" +" ) {\n" +" let allowance = self.allowances.read((owner, spender));\n" +" self.allowances.write((owner, spender), allowance - amount);\n" +" }\n" +"\n" +" fn approve_helper(\n" +" ref self: ContractState,\n" +" owner: ContractAddress,\n" +" spender: ContractAddress,\n" +" amount: felt252\n" +" ) {\n" +" assert(!spender.is_zero(), Errors::APPROVE_TO_ZERO);\n" +" self.allowances.write((owner, spender), amount);\n" +" self.emit(Approval { owner, spender, value: amount });\n" +" }\n" +"\n" +" fn mint(ref self: ContractState, recipient: ContractAddress, amount: " +"felt252) {\n" +" assert(!recipient.is_zero(), Errors::MINT_TO_ZERO);\n" +" let supply = self.total_supply.read() + amount; // What can go " +"wrong here?\n" +" self.total_supply.write(supply);\n" +" let balance = self.balances.read(recipient) + amount;\n" +" self.balances.write(recipient, amount);\n" +" self\n" +" .emit(\n" +" Event::Transfer(\n" +" Transfer {\n" +" from: contract_address_const::<0>(), to: " +"recipient, value: amount\n" +" }\n" +" )\n" +" );\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch01/erc20.md:224 msgid "" @@ -2218,30 +5026,143 @@ msgid "" "(https://cairo-by-example.com/examples/erc20/) ones." msgstr "" "还有一些其他的实现,比如 [Open Zeppelin](https://docs.openzeppelin.com/" -"contracts-cairo/0.7.0/erc20) 或者 [Cairo By Example](https://cairo-by-" -"example.com/examples/erc20/) 中的实现。" +"contracts-cairo/0.7.0/erc20) 或者 [Cairo By Example](https://cairo-by-example." +"com/examples/erc20/) 中的实现。" + +#: src/ch01/erc20.md:228 +msgid "
Last change: 2023-10-24
" +msgstr "
Last change: 2023-10-24
" + +#: src/ch01/constant-product-amm.md:1 +msgid "# Constant Product AMM" +msgstr "# 恒定乘积自动做市商" #: src/ch01/constant-product-amm.md:3 msgid "" -"This is the Cairo adaptation of the [Solidity by example Constant Product " -"AMM](https://solidity-by-example.org/defi/constant-product-amm/)." +"This is the Cairo adaptation of the [Solidity by example Constant Product AMM]" +"(https://solidity-by-example.org/defi/constant-product-amm/)." msgstr "" "这个是 用Cairo 改编的 [Solidity by example Constant Product AMM](https://" "solidity-by-example.org/defi/constant-product-amm/)." -#: src/ch01/constant-product-amm.md:32 +#: src/ch01/constant-product-amm.md:5 msgid "" -"// Fee 0 - 1000 (0% - 100%, 1 decimal places)\n" +"```rust\n" +"use starknet::ContractAddress;\n" +"\n" +"#[starknet::interface]\n" +"trait IConstantProductAmm {\n" +" fn swap(ref self: TContractState, token_in: ContractAddress, amount_in: " +"u256) -> u256;\n" +" fn add_liquidity(ref self: TContractState, amount0: u256, amount1: u256) -" +"> u256;\n" +" fn remove_liquidity(ref self: TContractState, shares: u256) -> (u256, " +"u256);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod ConstantProductAmm {\n" +" use core::traits::Into;\n" +" use openzeppelin::token::erc20::interface::{IERC20Dispatcher, " +"IERC20DispatcherTrait};\n" +" use starknet::{\n" +" ContractAddress, get_caller_address, get_contract_address, " +"contract_address_const\n" +" };\n" +" use integer::u256_sqrt;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" token0: IERC20Dispatcher,\n" +" token1: IERC20Dispatcher,\n" +" reserve0: u256,\n" +" reserve1: u256,\n" +" total_supply: u256,\n" +" balance_of: LegacyMap::,\n" +" // Fee 0 - 1000 (0% - 100%, 1 decimal places)\n" " // E.g. 3 = 0.3%\n" -msgstr "" - -#: src/ch01/constant-product-amm.md:41 -msgid "// assert(fee <= 1000, 'fee > 1000');\n" -msgstr "" - -#: src/ch01/constant-product-amm.md:107 -msgid "" -"// How much dy for dx?\n" +" fee: u16,\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(\n" +" ref self: ContractState, token0: ContractAddress, token1: " +"ContractAddress, fee: u16\n" +" ) {\n" +" // assert(fee <= 1000, 'fee > 1000');\n" +" self.token0.write(IERC20Dispatcher { contract_address: token0 });\n" +" self.token1.write(IERC20Dispatcher { contract_address: token1 });\n" +" self.fee.write(fee);\n" +" }\n" +"\n" +" #[generate_trait]\n" +" impl PrivateFunctions of PrivateFunctionsTrait {\n" +" fn _mint(ref self: ContractState, to: ContractAddress, amount: u256) " +"{\n" +" self.balance_of.write(to, self.balance_of.read(to) + amount);\n" +" self.total_supply.write(self.total_supply.read() + amount);\n" +" }\n" +"\n" +" fn _burn(ref self: ContractState, from: ContractAddress, amount: " +"u256) {\n" +" self.balance_of.write(from, self.balance_of.read(from) - " +"amount);\n" +" self.total_supply.write(self.total_supply.read() - amount);\n" +" }\n" +"\n" +" fn _update(ref self: ContractState, reserve0: u256, reserve1: u256) " +"{\n" +" self.reserve0.write(reserve0);\n" +" self.reserve1.write(reserve1);\n" +" }\n" +"\n" +" #[inline(always)]\n" +" fn select_token(self: @ContractState, token: ContractAddress) -> bool " +"{\n" +" assert(\n" +" token == self.token0.read().contract_address\n" +" || token == self.token1.read().contract_address,\n" +" 'invalid token'\n" +" );\n" +" token == self.token0.read().contract_address\n" +" }\n" +"\n" +" #[inline(always)]\n" +" fn min(x: u256, y: u256) -> u256 {\n" +" if (x <= y) {\n" +" x\n" +" } else {\n" +" y\n" +" }\n" +" }\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ConstantProductAmm of super::IConstantProductAmm {\n" +" fn swap(ref self: ContractState, token_in: ContractAddress, " +"amount_in: u256) -> u256 {\n" +" assert(amount_in > 0, 'amount in = 0');\n" +" let is_token0: bool = self.select_token(token_in);\n" +"\n" +" let (token0, token1): (IERC20Dispatcher, IERC20Dispatcher) = (\n" +" self.token0.read(), self.token1.read()\n" +" );\n" +" let (reserve0, reserve1): (u256, u256) = (self.reserve0.read(), " +"self.reserve1.read());\n" +" let (\n" +" token_in, token_out, reserve_in, reserve_out\n" +" ): (IERC20Dispatcher, IERC20Dispatcher, u256, u256) =\n" +" if (is_token0) {\n" +" (token0, token1, reserve0, reserve1)\n" +" } else {\n" +" (token1, token0, reserve1, reserve0)\n" +" };\n" +"\n" +" let caller = get_caller_address();\n" +" let this = get_contract_address();\n" +" token_in.transfer_from(caller, this, amount_in);\n" +"\n" +" // How much dy for dx?\n" " // xy = k\n" " // (x + dx)(y - dy) = k\n" " // y - dy = k / (x + dx)\n" @@ -2249,11 +5170,31 @@ msgid "" " // y - xy / (x + dx) = dy\n" " // (yx + ydx - xy) / (x + dx) = dy\n" " // ydx / (x + dx) = dy\n" -msgstr "" - -#: src/ch01/constant-product-amm.md:135 -msgid "" -"// How much dx, dy to add?\n" +"\n" +" let amount_in_with_fee = (amount_in * (1000 - self.fee.read()." +"into()) / 1000);\n" +" let amount_out = (reserve_out * amount_in_with_fee) / (reserve_in " +"+ amount_in_with_fee);\n" +"\n" +" token_out.transfer(caller, amount_out);\n" +"\n" +" self._update(self.token0.read().balance_of(this), self.token1." +"read().balance_of(this));\n" +" amount_out\n" +" }\n" +"\n" +" fn add_liquidity(ref self: ContractState, amount0: u256, amount1: " +"u256) -> u256 {\n" +" let caller = get_caller_address();\n" +" let this = get_contract_address();\n" +" let (token0, token1): (IERC20Dispatcher, IERC20Dispatcher) = (\n" +" self.token0.read(), self.token1.read()\n" +" );\n" +"\n" +" token0.transfer_from(caller, this, amount0);\n" +" token1.transfer_from(caller, this, amount1);\n" +"\n" +" // How much dx, dy to add?\n" " //\n" " // xy = k\n" " // (x + dx)(y + dy) = k'\n" @@ -2266,11 +5207,15 @@ msgid "" " //\n" " // x / y = dx / dy\n" " // dy = y / x * dx\n" -msgstr "" - -#: src/ch01/constant-product-amm.md:154 -msgid "" -"// How much shares to mint?\n" +"\n" +" let (reserve0, reserve1): (u256, u256) = (self.reserve0.read(), " +"self.reserve1.read());\n" +" if (reserve0 > 0 || reserve1 > 0) {\n" +" assert(reserve0 * amount1 == reserve1 * amount0, 'x / y != " +"dx / dy');\n" +" }\n" +"\n" +" // How much shares to mint?\n" " //\n" " // f(x, y) = value of liquidity\n" " // We will define f(x, y) = sqrt(xy)\n" @@ -2287,11 +5232,8 @@ msgid "" " // L1 * T = L0 * (T + s)\n" " //\n" " // (L1 - L0) * T / L0 = s\n" -msgstr "" - -#: src/ch01/constant-product-amm.md:171 -msgid "" -"// Claim\n" +"\n" +" // Claim\n" " // (L1 - L0) / L0 = dx / x = dy / y\n" " //\n" " // Proof\n" @@ -2323,11 +5265,332 @@ msgid "" " //\n" " // Finally\n" " // (L1 - L0) / L0 = dx / x = dy / y\n" +"\n" +" let total_supply = self.total_supply.read();\n" +" let shares = if (total_supply == 0) {\n" +" u256_sqrt(amount0 * amount1).into()\n" +" } else {\n" +" PrivateFunctions::min(\n" +" amount0 * total_supply / reserve0, amount1 * " +"total_supply / reserve1\n" +" )\n" +" };\n" +" assert(shares > 0, 'shares = 0');\n" +" self._mint(caller, shares);\n" +"\n" +" self._update(self.token0.read().balance_of(this), self.token1." +"read().balance_of(this));\n" +" shares\n" +" }\n" +"\n" +" fn remove_liquidity(ref self: ContractState, shares: u256) -> (u256, " +"u256) {\n" +" let caller = get_caller_address();\n" +" let this = get_contract_address();\n" +" let (token0, token1): (IERC20Dispatcher, IERC20Dispatcher) = (\n" +" self.token0.read(), self.token1.read()\n" +" );\n" +"\n" +" // Claim\n" +" // dx, dy = amount of liquidity to remove\n" +" // dx = s / T * x\n" +" // dy = s / T * y\n" +" //\n" +" // Proof\n" +" // Let's find dx, dy such that\n" +" // v / L = s / T\n" +" //\n" +" // where\n" +" // v = f(dx, dy) = sqrt(dxdy)\n" +" // L = total liquidity = sqrt(xy)\n" +" // s = shares\n" +" // T = total supply\n" +" //\n" +" // --- Equation 1 ---\n" +" // v = s / T * L\n" +" // sqrt(dxdy) = s / T * sqrt(xy)\n" +" //\n" +" // Amount of liquidity to remove must not change price so\n" +" // dx / dy = x / y\n" +" //\n" +" // replace dy = dx * y / x\n" +" // sqrt(dxdy) = sqrt(dx * dx * y / x) = dx * sqrt(y / x)\n" +" //\n" +" // Divide both sides of Equation 1 with sqrt(y / x)\n" +" // dx = s / T * sqrt(xy) / sqrt(y / x)\n" +" // = s / T * sqrt(x^2) = s / T * x\n" +" //\n" +" // Likewise\n" +" // dy = s / T * y\n" +"\n" +" // bal0 >= reserve0\n" +" // bal1 >= reserve1\n" +" let (bal0, bal1): (u256, u256) = (token0.balance_of(this), token1." +"balance_of(this));\n" +"\n" +" let total_supply = self.total_supply.read();\n" +" let (amount0, amount1): (u256, u256) = (\n" +" (shares * bal0) / total_supply, (shares * bal1) / " +"total_supply\n" +" );\n" +" assert(amount0 > 0 && amount1 > 0, 'amount0 or amount1 = 0');\n" +"\n" +" self._burn(caller, shares);\n" +" self._update(bal0 - amount0, bal1 - amount1);\n" +"\n" +" token0.transfer(caller, amount0);\n" +" token1.transfer(caller, amount1);\n" +" (amount0, amount1)\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" - -#: src/ch01/constant-product-amm.md:221 -msgid "" -"// Claim\n" +"```rust\n" +"use starknet::ContractAddress;\n" +"\n" +"#[starknet::interface]\n" +"trait IConstantProductAmm {\n" +" fn swap(ref self: TContractState, token_in: ContractAddress, amount_in: " +"u256) -> u256;\n" +" fn add_liquidity(ref self: TContractState, amount0: u256, amount1: u256) -" +"> u256;\n" +" fn remove_liquidity(ref self: TContractState, shares: u256) -> (u256, " +"u256);\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod ConstantProductAmm {\n" +" use core::traits::Into;\n" +" use openzeppelin::token::erc20::interface::{IERC20Dispatcher, " +"IERC20DispatcherTrait};\n" +" use starknet::{\n" +" ContractAddress, get_caller_address, get_contract_address, " +"contract_address_const\n" +" };\n" +" use integer::u256_sqrt;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" token0: IERC20Dispatcher,\n" +" token1: IERC20Dispatcher,\n" +" reserve0: u256,\n" +" reserve1: u256,\n" +" total_supply: u256,\n" +" balance_of: LegacyMap::,\n" +" // Fee 0 - 1000 (0% - 100%, 1 decimal places)\n" +" // E.g. 3 = 0.3%\n" +" fee: u16,\n" +" }\n" +"\n" +" #[constructor]\n" +" fn constructor(\n" +" ref self: ContractState, token0: ContractAddress, token1: " +"ContractAddress, fee: u16\n" +" ) {\n" +" // assert(fee <= 1000, 'fee > 1000');\n" +" self.token0.write(IERC20Dispatcher { contract_address: token0 });\n" +" self.token1.write(IERC20Dispatcher { contract_address: token1 });\n" +" self.fee.write(fee);\n" +" }\n" +"\n" +" #[generate_trait]\n" +" impl PrivateFunctions of PrivateFunctionsTrait {\n" +" fn _mint(ref self: ContractState, to: ContractAddress, amount: u256) " +"{\n" +" self.balance_of.write(to, self.balance_of.read(to) + amount);\n" +" self.total_supply.write(self.total_supply.read() + amount);\n" +" }\n" +"\n" +" fn _burn(ref self: ContractState, from: ContractAddress, amount: " +"u256) {\n" +" self.balance_of.write(from, self.balance_of.read(from) - " +"amount);\n" +" self.total_supply.write(self.total_supply.read() - amount);\n" +" }\n" +"\n" +" fn _update(ref self: ContractState, reserve0: u256, reserve1: u256) " +"{\n" +" self.reserve0.write(reserve0);\n" +" self.reserve1.write(reserve1);\n" +" }\n" +"\n" +" #[inline(always)]\n" +" fn select_token(self: @ContractState, token: ContractAddress) -> bool " +"{\n" +" assert(\n" +" token == self.token0.read().contract_address\n" +" || token == self.token1.read().contract_address,\n" +" 'invalid token'\n" +" );\n" +" token == self.token0.read().contract_address\n" +" }\n" +"\n" +" #[inline(always)]\n" +" fn min(x: u256, y: u256) -> u256 {\n" +" if (x <= y) {\n" +" x\n" +" } else {\n" +" y\n" +" }\n" +" }\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ConstantProductAmm of super::IConstantProductAmm {\n" +" fn swap(ref self: ContractState, token_in: ContractAddress, " +"amount_in: u256) -> u256 {\n" +" assert(amount_in > 0, 'amount in = 0');\n" +" let is_token0: bool = self.select_token(token_in);\n" +"\n" +" let (token0, token1): (IERC20Dispatcher, IERC20Dispatcher) = (\n" +" self.token0.read(), self.token1.read()\n" +" );\n" +" let (reserve0, reserve1): (u256, u256) = (self.reserve0.read(), " +"self.reserve1.read());\n" +" let (\n" +" token_in, token_out, reserve_in, reserve_out\n" +" ): (IERC20Dispatcher, IERC20Dispatcher, u256, u256) =\n" +" if (is_token0) {\n" +" (token0, token1, reserve0, reserve1)\n" +" } else {\n" +" (token1, token0, reserve1, reserve0)\n" +" };\n" +"\n" +" let caller = get_caller_address();\n" +" let this = get_contract_address();\n" +" token_in.transfer_from(caller, this, amount_in);\n" +"\n" +" // How much dy for dx?\n" +" // xy = k\n" +" // (x + dx)(y - dy) = k\n" +" // y - dy = k / (x + dx)\n" +" // y - k / (x + dx) = dy\n" +" // y - xy / (x + dx) = dy\n" +" // (yx + ydx - xy) / (x + dx) = dy\n" +" // ydx / (x + dx) = dy\n" +"\n" +" let amount_in_with_fee = (amount_in * (1000 - self.fee.read()." +"into()) / 1000);\n" +" let amount_out = (reserve_out * amount_in_with_fee) / (reserve_in " +"+ amount_in_with_fee);\n" +"\n" +" token_out.transfer(caller, amount_out);\n" +"\n" +" self._update(self.token0.read().balance_of(this), self.token1." +"read().balance_of(this));\n" +" amount_out\n" +" }\n" +"\n" +" fn add_liquidity(ref self: ContractState, amount0: u256, amount1: " +"u256) -> u256 {\n" +" let caller = get_caller_address();\n" +" let this = get_contract_address();\n" +" let (token0, token1): (IERC20Dispatcher, IERC20Dispatcher) = (\n" +" self.token0.read(), self.token1.read()\n" +" );\n" +"\n" +" token0.transfer_from(caller, this, amount0);\n" +" token1.transfer_from(caller, this, amount1);\n" +"\n" +" // How much dx, dy to add?\n" +" //\n" +" // xy = k\n" +" // (x + dx)(y + dy) = k'\n" +" //\n" +" // No price change, before and after adding liquidity\n" +" // x / y = (x + dx) / (y + dy)\n" +" //\n" +" // x(y + dy) = y(x + dx)\n" +" // x * dy = y * dx\n" +" //\n" +" // x / y = dx / dy\n" +" // dy = y / x * dx\n" +"\n" +" let (reserve0, reserve1): (u256, u256) = (self.reserve0.read(), " +"self.reserve1.read());\n" +" if (reserve0 > 0 || reserve1 > 0) {\n" +" assert(reserve0 * amount1 == reserve1 * amount0, 'x / y != " +"dx / dy');\n" +" }\n" +"\n" +" // How much shares to mint?\n" +" //\n" +" // f(x, y) = value of liquidity\n" +" // We will define f(x, y) = sqrt(xy)\n" +" //\n" +" // L0 = f(x, y)\n" +" // L1 = f(x + dx, y + dy)\n" +" // T = total shares\n" +" // s = shares to mint\n" +" //\n" +" // Total shares should increase proportional to increase in " +"liquidity\n" +" // L1 / L0 = (T + s) / T\n" +" //\n" +" // L1 * T = L0 * (T + s)\n" +" //\n" +" // (L1 - L0) * T / L0 = s\n" +"\n" +" // Claim\n" +" // (L1 - L0) / L0 = dx / x = dy / y\n" +" //\n" +" // Proof\n" +" // --- Equation 1 ---\n" +" // (L1 - L0) / L0 = (sqrt((x + dx)(y + dy)) - sqrt(xy)) / " +"sqrt(xy)\n" +" //\n" +" // dx / dy = x / y so replace dy = dx * y / x\n" +" //\n" +" // --- Equation 2 ---\n" +" // Equation 1 = (sqrt(xy + 2ydx + dx^2 * y / x) - sqrt(xy)) / " +"sqrt(xy)\n" +" //\n" +" // Multiply by sqrt(x) / sqrt(x)\n" +" // Equation 2 = (sqrt(x^2y + 2xydx + dx^2 * y) - sqrt(x^2y)) / " +"sqrt(x^2y)\n" +" // = (sqrt(y)(sqrt(x^2 + 2xdx + dx^2) - sqrt(x^2)) / " +"(sqrt(y)sqrt(x^2))\n" +" // sqrt(y) on top and bottom cancels out\n" +" //\n" +" // --- Equation 3 ---\n" +" // Equation 2 = (sqrt(x^2 + 2xdx + dx^2) - sqrt(x^2)) / " +"(sqrt(x^2)\n" +" // = (sqrt((x + dx)^2) - sqrt(x^2)) / sqrt(x^2)\n" +" // = ((x + dx) - x) / x\n" +" // = dx / x\n" +" // Since dx / dy = x / y,\n" +" // dx / x = dy / y\n" +" //\n" +" // Finally\n" +" // (L1 - L0) / L0 = dx / x = dy / y\n" +"\n" +" let total_supply = self.total_supply.read();\n" +" let shares = if (total_supply == 0) {\n" +" u256_sqrt(amount0 * amount1).into()\n" +" } else {\n" +" PrivateFunctions::min(\n" +" amount0 * total_supply / reserve0, amount1 * " +"total_supply / reserve1\n" +" )\n" +" };\n" +" assert(shares > 0, 'shares = 0');\n" +" self._mint(caller, shares);\n" +"\n" +" self._update(self.token0.read().balance_of(this), self.token1." +"read().balance_of(this));\n" +" shares\n" +" }\n" +"\n" +" fn remove_liquidity(ref self: ContractState, shares: u256) -> (u256, " +"u256) {\n" +" let caller = get_caller_address();\n" +" let this = get_contract_address();\n" +" let (token0, token1): (IERC20Dispatcher, IERC20Dispatcher) = (\n" +" self.token0.read(), self.token1.read()\n" +" );\n" +"\n" +" // Claim\n" " // dx, dy = amount of liquidity to remove\n" " // dx = s / T * x\n" " // dy = s / T * y\n" @@ -2358,13 +5621,29 @@ msgid "" " //\n" " // Likewise\n" " // dy = s / T * y\n" -msgstr "" - -#: src/ch01/constant-product-amm.md:253 -msgid "" -"// bal0 >= reserve0\n" +"\n" +" // bal0 >= reserve0\n" " // bal1 >= reserve1\n" -msgstr "" +" let (bal0, bal1): (u256, u256) = (token0.balance_of(this), token1." +"balance_of(this));\n" +"\n" +" let total_supply = self.total_supply.read();\n" +" let (amount0, amount1): (u256, u256) = (\n" +" (shares * bal0) / total_supply, (shares * bal1) / " +"total_supply\n" +" );\n" +" assert(amount0 > 0 && amount1 > 0, 'amount0 or amount1 = 0');\n" +"\n" +" self._burn(caller, shares);\n" +" self._update(bal0 - amount0, bal1 - amount1);\n" +"\n" +" token0.transfer(caller, amount0);\n" +" token1.transfer(caller, amount1);\n" +" (amount0, amount1)\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch01/constant-product-amm.md:274 msgid "" @@ -2377,15 +5656,18 @@ msgstr "" "com/NethermindEth/StarknetByExample/blob/main/listings/ch01-applications/" "constant_product_amm/src/constant_product_amm.cairo) 中尝试这个合约。" +#: src/ch02/write_to_any_slot.md:1 +msgid "# Writing to any storage slot" +msgstr "# 写入任何存储槽" + #: src/ch02/write_to_any_slot.md:3 -#, fuzzy msgid "" "On Starknet, a contract's storage is a map with 2^251 slots, where each slot " -"is a felt which is initialized to 0. The address of storage variables is " -"computed at compile time using the formula: `storage variable address := " -"pedersen(keccak(variable name), keys)`. Interactions with storage variables " -"are commonly performed using the `self.var.read()` and `self.var.write()` " -"functions." +"is a felt which is initialized to 0.\n" +"The address of storage variables is computed at compile time using the " +"formula: `storage variable address := pedersen(keccak(variable name), keys)`. " +"Interactions with storage variables are commonly performed using the `self." +"var.read()` and `self.var.write()` functions." msgstr "" "在Starknet上,一个合约的存储是一个拥有 2^251 个槽的map,每个槽是一个初始化为 " "0 的 felt。存储变量的地址在编译时通过公式计算得出:`存储变量地址 := " @@ -2393,10 +5675,9 @@ msgstr "" "和 `self.var.write()` 。" #: src/ch02/write_to_any_slot.md:6 -#, fuzzy msgid "" "Nevertheless, we can use the `storage_write_syscall` and " -"`storage_read_syscall` syscalls, to write to and read from any storage slot. " +"`storage_read_syscall` syscalls, to write to and read from any storage slot.\n" "This is useful when writing to storage variables that are not known at " "compile time, or to ensure that even if the contract is upgraded and the " "computation method of storage variable addresses changes, they remain " @@ -2404,8 +5685,8 @@ msgid "" msgstr "" "然而,我们可以使用 `storage_write_syscall` 和 `storage_read_syscall` 系统调" "用,来对任何存储槽进行写入和读取。\n" -"这在写入那些在编译时还未确定的存储变量时非常有用,这也可以确保即使合约升级且" -"存储变量地址的计算方法改变,这些变量仍然可访问。" +"这在写入那些在编译时还未确定的存储变量时非常有用,这也可以确保即使合约升级且存" +"储变量地址的计算方法改变,这些变量仍然可访问。" #: src/ch02/write_to_any_slot.md:9 msgid "" @@ -2415,15 +5696,111 @@ msgid "" "onchain computations. Once the address is computed, we use the storage " "syscalls to interact with it." msgstr "" -"在以下示例中,我们使用 Poseidon 哈希函数来计算存储变量的地址。Poseidon 是一" -"个 ZK 友好的哈希函数,比 Pedersen 更便宜、更快,是链上计算的绝佳选择。一旦地" -"址被计算出来,我们就使用存储的系统调用与之交互。" +"在以下示例中,我们使用 Poseidon 哈希函数来计算存储变量的地址。Poseidon 是一个 " +"ZK 友好的哈希函数,比 Pedersen 更便宜、更快,是链上计算的绝佳选择。一旦地址被" +"计算出来,我们就使用存储的系统调用与之交互。" -#: src/ch02/write_to_any_slot.md:49 +#: src/ch02/write_to_any_slot.md:11 msgid "" -"// By taking the 250 least significant bits of the hash output, we get a " -"valid 250bits storage address.\n" +"```rust\n" +"#[starknet::interface]\n" +"trait IWriteToAnySlots {\n" +" fn write_slot(ref self: TContractState, value: u32);\n" +" fn read_slot(self: @TContractState) -> u32;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod WriteToAnySlot {\n" +" use starknet::syscalls::{storage_read_syscall, storage_write_syscall};\n" +" use starknet::SyscallResultTrait;\n" +" use poseidon::poseidon_hash_span;\n" +" use starknet::storage_access::Felt252TryIntoStorageAddress;\n" +" use starknet::StorageAddress;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" const SLOT_NAME: felt252 = 'test_slot';\n" +"\n" +" #[abi(embed_v0)]\n" +" impl WriteToAnySlot of super::IWriteToAnySlots {\n" +" fn write_slot(ref self: ContractState, value: u32) {\n" +" storage_write_syscall(0, get_address_from_name(SLOT_NAME), value." +"into());\n" +" }\n" +"\n" +" fn read_slot(self: @ContractState) -> u32 {\n" +" storage_read_syscall(0, get_address_from_name(SLOT_NAME))\n" +" .unwrap_syscall()\n" +" .try_into()\n" +" .unwrap()\n" +" }\n" +" }\n" +" fn get_address_from_name(variable_name: felt252) -> StorageAddress {\n" +" let mut data: Array = ArrayTrait::new();\n" +" data.append(variable_name);\n" +" let hashed_name: felt252 = poseidon_hash_span(data.span());\n" +" let MASK_250: u256 = " +"0x03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;\n" +" // By taking the 250 least significant bits of the hash output, we " +"get a valid 250bits storage address.\n" +" let result: felt252 = (hashed_name.into() & MASK_250).try_into()." +"unwrap();\n" +" let result: StorageAddress = result.try_into().unwrap();\n" +" result\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait IWriteToAnySlots {\n" +" fn write_slot(ref self: TContractState, value: u32);\n" +" fn read_slot(self: @TContractState) -> u32;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod WriteToAnySlot {\n" +" use starknet::syscalls::{storage_read_syscall, storage_write_syscall};\n" +" use starknet::SyscallResultTrait;\n" +" use poseidon::poseidon_hash_span;\n" +" use starknet::storage_access::Felt252TryIntoStorageAddress;\n" +" use starknet::StorageAddress;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" const SLOT_NAME: felt252 = 'test_slot';\n" +"\n" +" #[abi(embed_v0)]\n" +" impl WriteToAnySlot of super::IWriteToAnySlots {\n" +" fn write_slot(ref self: ContractState, value: u32) {\n" +" storage_write_syscall(0, get_address_from_name(SLOT_NAME), value." +"into());\n" +" }\n" +"\n" +" fn read_slot(self: @ContractState) -> u32 {\n" +" storage_read_syscall(0, get_address_from_name(SLOT_NAME))\n" +" .unwrap_syscall()\n" +" .try_into()\n" +" .unwrap()\n" +" }\n" +" }\n" +" fn get_address_from_name(variable_name: felt252) -> StorageAddress {\n" +" let mut data: Array = ArrayTrait::new();\n" +" data.append(variable_name);\n" +" let hashed_name: felt252 = poseidon_hash_span(data.span());\n" +" let MASK_250: u256 = " +"0x03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;\n" +" // By taking the 250 least significant bits of the hash output, we " +"get a valid 250bits storage address.\n" +" let result: felt252 = (hashed_name.into() & MASK_250).try_into()." +"unwrap();\n" +" let result: StorageAddress = result.try_into().unwrap();\n" +" result\n" +" }\n" +"}\n" +"```" #: src/ch02/write_to_any_slot.md:56 msgid "" @@ -2441,6 +5818,10 @@ msgstr "" "blob/main/listings/ch02-advanced-concepts/write_to_any_slot/src/contract." "cairo) 中测试它." +#: src/ch02/storing_arrays.md:1 +msgid "# Storing Arrays" +msgstr "# 存储数组" + #: src/ch02/storing_arrays.md:3 msgid "" "On Starknet, complex values (e.g., tuples or structs), are stored in a " @@ -2452,58 +5833,180 @@ msgid "" "for storing arrays in Cairo, so you will need to write your own " "implementation of the `Store` trait for the type of array you wish to store." msgstr "" -"在Starknet上,复杂值(例如元组或结构体)存储在以该存储变量地址开头的连续段" -"中。复杂存储值的大小有 256 个元素的限制,这意味着要在存储中存储超过 255 个元" -"素的数组,我们需要将其分割为大小 `n <= 255` 的段,并将这些段存储在多个存储地" -"址中。目前 Cairo 没有原生支持存储数组,所以你需要为你希望存储的数组类型实现自" -"己的 `Store` 特性。" +"在Starknet上,复杂值(例如元组或结构体)存储在以该存储变量地址开头的连续段中。" +"复杂存储值的大小有 256 个元素的限制,这意味着要在存储中存储超过 255 个元素的数" +"组,我们需要将其分割为大小 `n <= 255` 的段,并将这些段存储在多个存储地址中。目" +"前 Cairo 没有原生支持存储数组,所以你需要为你希望存储的数组类型实现自己的 " +"`Store` 特性。" #: src/ch02/storing_arrays.md:5 -#, fuzzy msgid "" -"Note: While storing arrays in storage is possible, it is not always " +"> Note: While storing arrays in storage is possible, it is not always " "recommended, as the read and write operations can get very costly. For " -"example, reading an array of size `n` requires `n` storage reads, and " -"writing to an array of size `n` requires `n` storage writes. If you only " -"need to access a single element of the array at a time, it is recommended to " -"use a `LegacyMap` and store the length in another variable instead." +"example, reading an array of size `n` requires `n` storage reads, and writing " +"to an array of size `n` requires `n` storage writes. If you only need to " +"access a single element of the array at a time, it is recommended to use a " +"`LegacyMap` and store the length in another variable instead." msgstr "" -"> 注:虽然在存储中保存数组是可行的,但并不总是推荐这么做,因为读写操作的成本" -"可能非常高。例如,读取一个大小为 n 的数组需要进行 `n` 次存储读取,而向一个大" -"小为 `n` 的数组写入需要进行 `n` 次存储写入。如果你只需要一次访问数组中的一个" -"元素,建议使用 `LegacyMap` 并在另一个变量中存储数组长度。" +"> 注:虽然在存储中保存数组是可行的,但并不总是推荐这么做,因为读写操作的成本可" +"能非常高。例如,读取一个大小为 n 的数组需要进行 `n` 次存储读取,而向一个大小" +"为 `n` 的数组写入需要进行 `n` 次存储写入。如果你只需要一次访问数组中的一个元" +"素,建议使用 `LegacyMap` 并在另一个变量中存储数组长度。" #: src/ch02/storing_arrays.md:7 msgid "" "The following example demonstrates how to write a simple implementation of " -"the `StorageAccess` trait for the `Array` type, allowing us to " -"store arrays of up to 255 `felt252` elements." +"the `StorageAccess` trait for the `Array` type, allowing us to store " +"arrays of up to 255 `felt252` elements." msgstr "" "以下示例展示了如何为 `Array` 类型实现一个简单的 `StorageAccess` 特" "性,使我们能够存储多达 255 个 `felt252` 元素的数组。" -#: src/ch02/storing_arrays.md:26 +#: src/ch02/storing_arrays.md:9 msgid "" -"// Read the stored array's length. If the length is superior to 255, the " -"read will fail.\n" -msgstr "" - -#: src/ch02/storing_arrays.md:31 -msgid "" -"// Sequentially read all stored elements and append them to the array.\n" -msgstr "" - -#: src/ch02/storing_arrays.md:43 -msgid "// Return the array.\n" -msgstr "" - -#: src/ch02/storing_arrays.md:50 -msgid "// // Store the length of the array in the first storage slot.\n" -msgstr "" - -#: src/ch02/storing_arrays.md:55 -msgid "// Store the array elements sequentially\n" +"```rust\n" +"impl StoreFelt252Array of Store> {\n" +" fn read(address_domain: u32, base: StorageBaseAddress) -> " +"SyscallResult> {\n" +" StoreFelt252Array::read_at_offset(address_domain, base, 0)\n" +" }\n" +"\n" +" fn write(\n" +" address_domain: u32, base: StorageBaseAddress, value: Array\n" +" ) -> SyscallResult<()> {\n" +" StoreFelt252Array::write_at_offset(address_domain, base, 0, value)\n" +" }\n" +"\n" +" fn read_at_offset(\n" +" address_domain: u32, base: StorageBaseAddress, mut offset: u8\n" +" ) -> SyscallResult> {\n" +" let mut arr: Array = ArrayTrait::new();\n" +"\n" +" // Read the stored array's length. If the length is superior to 255, " +"the read will fail.\n" +" let len: u8 = Store::::read_at_offset(address_domain, base, " +"offset)\n" +" .expect('Storage Span too large');\n" +" offset += 1;\n" +"\n" +" // Sequentially read all stored elements and append them to the " +"array.\n" +" let exit = len + offset;\n" +" loop {\n" +" if offset >= exit {\n" +" break;\n" +" }\n" +"\n" +" let value = Store::::read_at_offset(address_domain, " +"base, offset).unwrap();\n" +" arr.append(value);\n" +" offset += Store::::size();\n" +" };\n" +"\n" +" // Return the array.\n" +" Result::Ok(arr)\n" +" }\n" +"\n" +" fn write_at_offset(\n" +" address_domain: u32, base: StorageBaseAddress, mut offset: u8, mut " +"value: Array\n" +" ) -> SyscallResult<()> {\n" +" // // Store the length of the array in the first storage slot.\n" +" let len: u8 = value.len().try_into().expect('Storage - Span too " +"large');\n" +" Store::::write_at_offset(address_domain, base, offset, len);\n" +" offset += 1;\n" +"\n" +" // Store the array elements sequentially\n" +" loop {\n" +" match value.pop_front() {\n" +" Option::Some(element) => {\n" +" Store::::write_at_offset(address_domain, base, " +"offset, element);\n" +" offset += Store::::size();\n" +" },\n" +" Option::None(_) => { break Result::Ok(()); }\n" +" };\n" +" }\n" +" }\n" +"\n" +" fn size() -> u8 {\n" +" 255 * Store::::size()\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"impl StoreFelt252Array of Store> {\n" +" fn read(address_domain: u32, base: StorageBaseAddress) -> " +"SyscallResult> {\n" +" StoreFelt252Array::read_at_offset(address_domain, base, 0)\n" +" }\n" +"\n" +" fn write(\n" +" address_domain: u32, base: StorageBaseAddress, value: Array\n" +" ) -> SyscallResult<()> {\n" +" StoreFelt252Array::write_at_offset(address_domain, base, 0, value)\n" +" }\n" +"\n" +" fn read_at_offset(\n" +" address_domain: u32, base: StorageBaseAddress, mut offset: u8\n" +" ) -> SyscallResult> {\n" +" let mut arr: Array = ArrayTrait::new();\n" +"\n" +" // Read the stored array's length. If the length is superior to 255, " +"the read will fail.\n" +" let len: u8 = Store::::read_at_offset(address_domain, base, " +"offset)\n" +" .expect('Storage Span too large');\n" +" offset += 1;\n" +"\n" +" // Sequentially read all stored elements and append them to the " +"array.\n" +" let exit = len + offset;\n" +" loop {\n" +" if offset >= exit {\n" +" break;\n" +" }\n" +"\n" +" let value = Store::::read_at_offset(address_domain, " +"base, offset).unwrap();\n" +" arr.append(value);\n" +" offset += Store::::size();\n" +" };\n" +"\n" +" // Return the array.\n" +" Result::Ok(arr)\n" +" }\n" +"\n" +" fn write_at_offset(\n" +" address_domain: u32, base: StorageBaseAddress, mut offset: u8, mut " +"value: Array\n" +" ) -> SyscallResult<()> {\n" +" // // Store the length of the array in the first storage slot.\n" +" let len: u8 = value.len().try_into().expect('Storage - Span too " +"large');\n" +" Store::::write_at_offset(address_domain, base, offset, len);\n" +" offset += 1;\n" +"\n" +" // Store the array elements sequentially\n" +" loop {\n" +" match value.pop_front() {\n" +" Option::Some(element) => {\n" +" Store::::write_at_offset(address_domain, base, " +"offset, element);\n" +" offset += Store::::size();\n" +" },\n" +" Option::None(_) => { break Result::Ok(()); }\n" +" };\n" +" }\n" +" }\n" +"\n" +" fn size() -> u8 {\n" +" 255 * Store::::size()\n" +" }\n" +"}\n" +"```" #: src/ch02/storing_arrays.md:73 msgid "" @@ -2511,6 +6014,66 @@ msgid "" "arrays in storage:" msgstr "您可以在合约中导入上面的实现方式,并使用它来在存储中存储数组:" +#: src/ch02/storing_arrays.md:75 +msgid "" +"```rust\n" +"#[starknet::interface]\n" +"trait IStoreArrayContract {\n" +" fn store_array(ref self: TContractState, arr: Array);\n" +" fn read_array(self: @TContractState) -> Array;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod StoreArrayContract {\n" +" use super::StoreFelt252Array;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" arr: Array\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl StoreArrayImpl of super::IStoreArrayContract {\n" +" fn store_array(ref self: ContractState, arr: Array) {\n" +" self.arr.write(arr);\n" +" }\n" +"\n" +" fn read_array(self: @ContractState) -> Array {\n" +" self.arr.read()\n" +" }\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait IStoreArrayContract {\n" +" fn store_array(ref self: TContractState, arr: Array);\n" +" fn read_array(self: @TContractState) -> Array;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod StoreArrayContract {\n" +" use super::StoreFelt252Array;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" arr: Array\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl StoreArrayImpl of super::IStoreArrayContract {\n" +" fn store_array(ref self: ContractState, arr: Array) {\n" +" self.arr.write(arr);\n" +" }\n" +"\n" +" fn read_array(self: @ContractState) -> Array {\n" +" self.arr.read()\n" +" }\n" +" }\n" +"}\n" +"```" + #: src/ch02/storing_arrays.md:103 msgid "" "Visit contract on [Voyager](https://goerli.voyager.online/" @@ -2523,35 +6086,108 @@ msgstr "" "contract/0x008F8069a3Fcd7691Db46Dc3b6F9D2C0436f9200E861330957Fd780A3595da86) " "上的合约,或者在 [Remix](https://remix.ethereum.org/?" "#activate=Starknet&url=https://github.com/NethermindEth/StarknetByExample/" -"blob/main/listings/ch02-advanced-concepts/storing_arrays/src/contract.cairo)" -"上测试." +"blob/main/listings/ch02-advanced-concepts/storing_arrays/src/contract.cairo)上" +"测试." #: src/ch02/struct-mapping-key.md:1 -#, fuzzy -msgid "Structs as mapping keys" +msgid "# Structs as mapping keys" msgstr "# 结构体作为映射键" #: src/ch02/struct-mapping-key.md:3 msgid "" -"In order to use structs as mapping keys, you can use `#[derive(Hash)]` on " -"the struct definition. This will automatically generate a hash function for " -"the struct that can be used to represent the struct as a key in a " -"`LegacyMap`." +"In order to use structs as mapping keys, you can use `#[derive(Hash)]` on the " +"struct definition. This will automatically generate a hash function for the " +"struct that can be used to represent the struct as a key in a `LegacyMap`." msgstr "" -"为了使用结构体作为映射键,您可以在结构体定义上使用 `#[derive(Hash)]`。这将为" -"结构体自动生成一个哈希函数,可以在 `LegacyMap` 中将该结构体作为键来使用。" +"为了使用结构体作为映射键,您可以在结构体定义上使用 `#[derive(Hash)]`。这将为结" +"构体自动生成一个哈希函数,可以在 `LegacyMap` 中将该结构体作为键来使用。" #: src/ch02/struct-mapping-key.md:5 -#, fuzzy msgid "" -"Consider the following example in which we would like to use an object of " +"Consider the following example in which we would like to use an object of\n" "type `Pet` as a key in a `LegacyMap`. The `Pet` struct has three fields: " "`name`, `age` and `owner`. We consider that the combination of these three " "fields uniquely identifies a pet." msgstr "" -"考虑以下示例,我们希望使用类型为 `Pet` 的对象作为 `LegacyMap` 中的键。`Pet` " -"结构体有三个字段:`name` 、`age` 和 `owner`。假设这三个字段的组合能唯一地标识" -"一只宠物。" +"考虑以下示例,我们希望使用类型为 `Pet` 的对象作为 `LegacyMap` 中的键。`Pet` 结" +"构体有三个字段:`name` 、`age` 和 `owner`。假设这三个字段的组合能唯一地标识一" +"只宠物。" + +#: src/ch02/struct-mapping-key.md:8 +msgid "" +"```rust\n" +"#[derive(Copy, Drop, Serde, Hash)]\n" +"struct Pet {\n" +" name: felt252,\n" +" age: u8,\n" +" owner: felt252,\n" +"}\n" +"\n" +"#[starknet::interface]\n" +"trait IPetRegistry {\n" +" fn register_pet(ref self: TContractState, key: Pet, timestamp: u64);\n" +" fn get_registration_date(self: @TContractState, key: Pet) -> u64;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod PetRegistry {\n" +" use hash::{HashStateTrait, Hash};\n" +" use super::Pet;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" registration_time: LegacyMap::,\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl PetRegistry of super::IPetRegistry {\n" +" fn register_pet(ref self: ContractState, key: Pet, timestamp: u64) {\n" +" self.registration_time.write(key, timestamp);\n" +" }\n" +"\n" +" fn get_registration_date(self: @ContractState, key: Pet) -> u64 {\n" +" self.registration_time.read(key)\n" +" }\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"#[derive(Copy, Drop, Serde, Hash)]\n" +"struct Pet {\n" +" name: felt252,\n" +" age: u8,\n" +" owner: felt252,\n" +"}\n" +"\n" +"#[starknet::interface]\n" +"trait IPetRegistry {\n" +" fn register_pet(ref self: TContractState, key: Pet, timestamp: u64);\n" +" fn get_registration_date(self: @TContractState, key: Pet) -> u64;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod PetRegistry {\n" +" use hash::{HashStateTrait, Hash};\n" +" use super::Pet;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" registration_time: LegacyMap::,\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl PetRegistry of super::IPetRegistry {\n" +" fn register_pet(ref self: ContractState, key: Pet, timestamp: u64) {\n" +" self.registration_time.write(key, timestamp);\n" +" }\n" +"\n" +" fn get_registration_date(self: @ContractState, key: Pet) -> u64 {\n" +" self.registration_time.read(key)\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch02/struct-mapping-key.md:45 msgid "" @@ -2561,8 +6197,12 @@ msgid "" "cairo)." msgstr "" "在 [Remix](https://remix.ethereum.org/?#activate=Starknet&url=https://github." -"com/NethermindEth/StarknetByExample/blob/main/listings/ch02-advanced-" -"concepts/struct_as_mapping_key/src/contract.cairo) 上测试这个合约." +"com/NethermindEth/StarknetByExample/blob/main/listings/ch02-advanced-concepts/" +"struct_as_mapping_key/src/contract.cairo) 上测试这个合约." + +#: src/ch02/hash-solidity-compatible.md:1 +msgid "# Hash Solidity Compatible" +msgstr "# 兼容Hash Solidity" #: src/ch02/hash-solidity-compatible.md:3 msgid "" @@ -2573,21 +6213,89 @@ msgid "" "result with `u128_byte_reverse`." msgstr "" "这个合约展示了在 Cairo 中进行 Keccak 哈希处理以匹配 Solidity 的 keccak256。尽" -"管两者都使用 Keccak,但它们的字节序不同:Cairo 是小端序,Solidity 是大端序。" -"该合约通过使用 `keccak_u256s_be_inputs` 以大端序进行哈希处理,并使用 " +"管两者都使用 Keccak,但它们的字节序不同:Cairo 是小端序,Solidity 是大端序。该" +"合约通过使用 `keccak_u256s_be_inputs` 以大端序进行哈希处理,并使用 " "`u128_byte_reverse` 反转结果的字节来实现兼容。" -#: src/ch02/hash-solidity-compatible.md:27 -msgid "// Split the hashed value into two 128-bit segments\n" -msgstr "" - -#: src/ch02/hash-solidity-compatible.md:31 -msgid "// Reverse each 128-bit segment\n" -msgstr "" - -#: src/ch02/hash-solidity-compatible.md:35 -msgid "// Reverse merge the reversed segments back into a u256 value\n" +#: src/ch02/hash-solidity-compatible.md:7 +msgid "" +"```rust\n" +"#[starknet::interface]\n" +"trait ISolidityHashExample {\n" +" fn hash_data(ref self: TContractState, input_data: Span) -> u256;\n" +"}\n" +"\n" +"\n" +"#[starknet::contract]\n" +"mod SolidityHashExample {\n" +" use keccak::{keccak_u256s_be_inputs};\n" +" use array::Span;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" #[abi(embed_v0)]\n" +" impl SolidityHashExample of super::ISolidityHashExample {\n" +" fn hash_data(ref self: ContractState, input_data: Span) -> u256 " +"{\n" +" let hashed = keccak_u256s_be_inputs(input_data);\n" +"\n" +" // Split the hashed value into two 128-bit segments\n" +" let low: u128 = hashed.low;\n" +" let high: u128 = hashed.high;\n" +"\n" +" // Reverse each 128-bit segment\n" +" let reversed_low = integer::u128_byte_reverse(low);\n" +" let reversed_high = integer::u128_byte_reverse(high);\n" +"\n" +" // Reverse merge the reversed segments back into a u256 value\n" +" let compatible_hash = u256 { low: reversed_high, high: " +"reversed_low };\n" +"\n" +" compatible_hash\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait ISolidityHashExample {\n" +" fn hash_data(ref self: TContractState, input_data: Span) -> u256;\n" +"}\n" +"\n" +"\n" +"#[starknet::contract]\n" +"mod SolidityHashExample {\n" +" use keccak::{keccak_u256s_be_inputs};\n" +" use array::Span;\n" +"\n" +" #[storage]\n" +" struct Storage {}\n" +"\n" +" #[abi(embed_v0)]\n" +" impl SolidityHashExample of super::ISolidityHashExample {\n" +" fn hash_data(ref self: ContractState, input_data: Span) -> u256 " +"{\n" +" let hashed = keccak_u256s_be_inputs(input_data);\n" +"\n" +" // Split the hashed value into two 128-bit segments\n" +" let low: u128 = hashed.low;\n" +" let high: u128 = hashed.high;\n" +"\n" +" // Reverse each 128-bit segment\n" +" let reversed_low = integer::u128_byte_reverse(low);\n" +" let reversed_high = integer::u128_byte_reverse(high);\n" +"\n" +" // Reverse merge the reversed segments back into a u256 value\n" +" let compatible_hash = u256 { low: reversed_high, high: " +"reversed_low };\n" +"\n" +" compatible_hash\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch02/hash-solidity-compatible.md:44 msgid "" @@ -2597,43 +6305,49 @@ msgid "" "contract.cairo)." msgstr "" "在 [Remix](https://remix.ethereum.org/?#activate=Starknet&url=https://github." -"com/NethermindEth/StarknetByExample/blob/main/listings/ch02-advanced-" -"concepts/hash_solidity_compatible/src/contract.cairo) 上测试这个合约." +"com/NethermindEth/StarknetByExample/blob/main/listings/ch02-advanced-concepts/" +"hash_solidity_compatible/src/contract.cairo) 上测试这个合约." + +#: src/ch02/hash-solidity-compatible.md:46 +msgid "
Last change: 2023-11-21
" +msgstr "
Last change: 2023-11-21
" + +#: src/ch02/optimisations/optimisations.md:1 +msgid "# Optimisations " +msgstr "# 优化" #: src/ch02/optimisations/optimisations.md:3 msgid "A collection of optimisation patterns to save gas and steps." msgstr "这里列出了一系列优化模式,用以节省Gas和计算步骤。" #: src/ch02/optimisations/store_using_packing.md:1 -#, fuzzy -msgid "Storage optimisation" +msgid "# Storage optimisation " msgstr "# 存储优化" #: src/ch02/optimisations/store_using_packing.md:3 -#, fuzzy msgid "" "A smart contract has a limited amount of **storage slots**. Each slot can " -"store a single `felt252` value. Writing to a storage slot has a cost, so we " -"want to use as few storage slots as possible." +"store a single `felt252` value.\n" +"Writing to a storage slot has a cost, so we want to use as few storage slots " +"as possible." msgstr "" "智能合约只有有限的**存储槽位**。每个槽位可以存储一个 `felt252` 值。\n" "写入一个存储槽位会产生成本,因此我们希望尽可能少地使用存储槽位。" #: src/ch02/optimisations/store_using_packing.md:6 -#, fuzzy msgid "" "In Cairo, every type is derived from the `felt252` type, which uses 252 bits " -"to store a value. This design is quite simple, but it does have a drawback: " -"it is not storage efficient. For example, if we want to store a `u8` value, " -"we need to use an entire slot, even though we only need 8 bits." +"to store a value.\n" +"This design is quite simple, but it does have a drawback: it is not storage " +"efficient. For example, if we want to store a `u8` value, we need to use an " +"entire slot, even though we only need 8 bits." msgstr "" "在 Cairo 中,每种类型都源自 `felt252` 类型,它使用 252 位来存储一个值。\n" "这种设计相当简单,但它有一个缺点:它在存储效率方面并不高。例如,如果要存储一" "个 `u8` 值,我们需要使用整个槽位,尽管我们只需要 8 位。" #: src/ch02/optimisations/store_using_packing.md:9 -#, fuzzy -msgid "Packing" +msgid "## Packing" msgstr "## 打包" #: src/ch02/optimisations/store_using_packing.md:11 @@ -2644,19 +6358,19 @@ msgid "" "multiple values." msgstr "" "当存储多个值时,我们可以使用一种称为**打包(packing)**的技术。打包是一种允许我" -"们在单个 felt 值中存储多个值的技术。这是通过使用 felt 值的位来存储多个值来实" -"现的。" +"们在单个 felt 值中存储多个值的技术。这是通过使用 felt 值的位来存储多个值来实现" +"的。" #: src/ch02/optimisations/store_using_packing.md:13 msgid "" -"For example, if we want to store two `u8` values, we can use the first 8 " -"bits of the felt value to store the first `u8` value, and the last 8 bits to " -"store the second `u8` value. This way, we can store two `u8` values in a " -"single felt value." +"For example, if we want to store two `u8` values, we can use the first 8 bits " +"of the felt value to store the first `u8` value, and the last 8 bits to store " +"the second `u8` value. This way, we can store two `u8` values in a single " +"felt value." msgstr "" "例如,如果我们想存储两个 `u8` 值,我们可以使用 felt 值的前 8 位来存储第一个 " -"`u8` 值,而使用后 8 位来存储第二个 `u8` 值。这样,我们就可以在单个 felt 值中" -"存储两个 `u8` 值。" +"`u8` 值,而使用后 8 位来存储第二个 `u8` 值。这样,我们就可以在单个 felt 值中存" +"储两个 `u8` 值。" #: src/ch02/optimisations/store_using_packing.md:15 msgid "" @@ -2665,38 +6379,170 @@ msgid "" msgstr "" "Cairo 提供了一个内置的打包存储功能,您可以通过 `StorePacking` 特性来使用它。" +#: src/ch02/optimisations/store_using_packing.md:17 +msgid "" +"```rust\n" +"trait StorePacking {\n" +" fn pack(value: T) -> PackedT;\n" +" fn unpack(value: PackedT) -> T;\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"trait StorePacking {\n" +" fn pack(value: T) -> PackedT;\n" +" fn unpack(value: PackedT) -> T;\n" +"}\n" +"```" + #: src/ch02/optimisations/store_using_packing.md:24 msgid "" -"This allows to store the type `T` by first packing it into the type " -"`PackedT` with the `pack` function, and then storing the `PackedT` value " -"with it's `Store` implementation. When reading the value, we first retrieve " -"the `PackedT` value, and then unpack it into the type `T` using the `unpack` " +"This allows to store the type `T` by first packing it into the type `PackedT` " +"with the `pack` function, and then storing the `PackedT` value with it's " +"`Store` implementation. When reading the value, we first retrieve the " +"`PackedT` value, and then unpack it into the type `T` using the `unpack` " "function." msgstr "" "这允许通过首先使用 `pack` 函数将类型 `T` 打包成 `PackedT` 类型,然后使用其 " -"`Store` 实现来存储 `PackedT` 值。在读取值时,我们首先获取 `PackedT` 值,然后" -"使用 `unpack` 函数将其解包为类型 `T`。" +"`Store` 实现来存储 `PackedT` 值。在读取值时,我们首先获取 `PackedT` 值,然后使" +"用 `unpack` 函数将其解包为类型 `T`。" #: src/ch02/optimisations/store_using_packing.md:26 msgid "" "Here's an example of storing a `Time` struct with two `u8` values using the " "`StorePacking` trait:" msgstr "" -"以下是一个使用 `StorePacking` 特性存储包含两个 `u8` 值的 `Time` 结构体的示" -"例:" +"以下是一个使用 `StorePacking` 特性存储包含两个 `u8` 值的 `Time` 结构体的示例:" -#: src/ch02/optimisations/store_using_packing.md:75 +#: src/ch02/optimisations/store_using_packing.md:28 msgid "" -"// This will call the pack method of the TimePackable trait\n" +"```rust\n" +"#[starknet::interface]\n" +"trait ITime {\n" +" fn set(ref self: TContractState, value: TimeContract::Time);\n" +" fn get(self: @TContractState) -> TimeContract::Time;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod TimeContract {\n" +" use starknet::storage_access::StorePacking;\n" +" use integer::{\n" +" U8IntoFelt252, Felt252TryIntoU16, U16DivRem, u16_as_non_zero, " +"U16IntoFelt252,\n" +" Felt252TryIntoU8\n" +" };\n" +" use traits::{Into, TryInto, DivRem};\n" +" use option::OptionTrait;\n" +" use serde::Serde;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" time: Time\n" +" }\n" +"\n" +" #[derive(Copy, Serde, Drop)]\n" +" struct Time {\n" +" hour: u8,\n" +" minute: u8\n" +" }\n" +"\n" +" impl TimePackable of StorePacking {\n" +" fn pack(value: Time) -> felt252 {\n" +" let msb: felt252 = 256 * value.hour.into();\n" +" let lsb: felt252 = value.minute.into();\n" +" return msb + lsb;\n" +" }\n" +" fn unpack(value: felt252) -> Time {\n" +" let value: u16 = value.try_into().unwrap();\n" +" let (q, r) = U16DivRem::div_rem(value, u16_as_non_zero(256));\n" +" let hour: u8 = Into::::into(q).try_into()." +"unwrap();\n" +" let minute: u8 = Into::::into(r).try_into()." +"unwrap();\n" +" return Time { hour, minute };\n" +" }\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl TimeContract of super::ITime {\n" +" fn set(ref self: ContractState, value: Time) {\n" +" // This will call the pack method of the TimePackable trait\n" " // and store the resulting felt252\n" +" self.time.write(value);\n" +" }\n" +" fn get(self: @ContractState) -> Time {\n" +" // This will read the felt252 value from storage\n" +" // and return the result of the unpack method of the TimePackable " +"trait\n" +" return self.time.read();\n" +" }\n" +" }\n" +"}\n" +"```" msgstr "" - -#: src/ch02/optimisations/store_using_packing.md:80 -msgid "" -"// This will read the felt252 value from storage\n" -" // and return the result of the unpack method of the " -"TimePackable trait\n" -msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait ITime {\n" +" fn set(ref self: TContractState, value: TimeContract::Time);\n" +" fn get(self: @TContractState) -> TimeContract::Time;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod TimeContract {\n" +" use starknet::storage_access::StorePacking;\n" +" use integer::{\n" +" U8IntoFelt252, Felt252TryIntoU16, U16DivRem, u16_as_non_zero, " +"U16IntoFelt252,\n" +" Felt252TryIntoU8\n" +" };\n" +" use traits::{Into, TryInto, DivRem};\n" +" use option::OptionTrait;\n" +" use serde::Serde;\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" time: Time\n" +" }\n" +"\n" +" #[derive(Copy, Serde, Drop)]\n" +" struct Time {\n" +" hour: u8,\n" +" minute: u8\n" +" }\n" +"\n" +" impl TimePackable of StorePacking {\n" +" fn pack(value: Time) -> felt252 {\n" +" let msb: felt252 = 256 * value.hour.into();\n" +" let lsb: felt252 = value.minute.into();\n" +" return msb + lsb;\n" +" }\n" +" fn unpack(value: felt252) -> Time {\n" +" let value: u16 = value.try_into().unwrap();\n" +" let (q, r) = U16DivRem::div_rem(value, u16_as_non_zero(256));\n" +" let hour: u8 = Into::::into(q).try_into()." +"unwrap();\n" +" let minute: u8 = Into::::into(r).try_into()." +"unwrap();\n" +" return Time { hour, minute };\n" +" }\n" +" }\n" +"\n" +" #[abi(embed_v0)]\n" +" impl TimeContract of super::ITime {\n" +" fn set(ref self: ContractState, value: Time) {\n" +" // This will call the pack method of the TimePackable trait\n" +" // and store the resulting felt252\n" +" self.time.write(value);\n" +" }\n" +" fn get(self: @ContractState) -> Time {\n" +" // This will read the felt252 value from storage\n" +" // and return the result of the unpack method of the TimePackable " +"trait\n" +" return self.time.read();\n" +" }\n" +" }\n" +"}\n" +"```" #: src/ch02/optimisations/store_using_packing.md:88 msgid "" @@ -2707,8 +6553,12 @@ msgid "" msgstr "" "在 [Remix](https://remix.ethereum.org/?#activate=Starknet-cairo1-" "compiler&url=https://github.com/NethermindEth/StarknetByExample/blob/main/" -"listings/ch02-advanced-concepts/store_using_packing/src/contract.cairo) 上测" -"试这个合约." +"listings/ch02-advanced-concepts/store_using_packing/src/contract.cairo) 上测试" +"这个合约." + +#: src/ch02/list.md:1 +msgid "# List" +msgstr "# 列表" #: src/ch02/list.md:3 msgid "" @@ -2721,19 +6571,59 @@ msgstr "" "main/src/storage) 获取更多详细信息。" #: src/ch02/list.md:5 -#, fuzzy -msgid "What is `List`?" +msgid "## What is `List`?" msgstr "## `List`是什么?" #: src/ch02/list.md:7 msgid "An ordered sequence of values that can be used in Starknet storage:" msgstr "可以在 Starknet 存储中使用的有序值序列:" +#: src/ch02/list.md:9 +msgid "" +"```rust\n" +"#[storage]\n" +"stuct Storage {\n" +" amounts: List\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"#[storage]\n" +"stuct Storage {\n" +" amounts: List\n" +"}\n" +"```" + #: src/ch02/list.md:16 -#, fuzzy -msgid "Interface" +msgid "### Interface" msgstr "### 接口" +#: src/ch02/list.md:18 +msgid "" +"```rust\n" +"trait ListTrait {\n" +" fn len(self: @List) -> u32;\n" +" fn is_empty(self: @List) -> bool;\n" +" fn append(ref self: List, value: T) -> u32;\n" +" fn get(self: @List, index: u32) -> Option;\n" +" fn set(ref self: List, index: u32, value: T);\n" +" fn pop_front(ref self: List) -> Option;\n" +" fn array(self: @List) -> Array;\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"trait ListTrait {\n" +" fn len(self: @List) -> u32;\n" +" fn is_empty(self: @List) -> bool;\n" +" fn append(ref self: List, value: T) -> u32;\n" +" fn get(self: @List, index: u32) -> Option;\n" +" fn set(ref self: List, index: u32, value: T);\n" +" fn pop_front(ref self: List) -> Option;\n" +" fn array(self: @List) -> Array;\n" +"}\n" +"```" + #: src/ch02/list.md:30 msgid "" "`List` also implements `IndexView` so you can use the familiar bracket " @@ -2741,6 +6631,16 @@ msgid "" msgstr "" "`List` 还实现了 `IndexView`,因此您可以使用熟悉的方括号表示法来访问其成员:" +#: src/ch02/list.md:32 +msgid "" +"```rust\n" +"let second = self.amounts.read()[1];\n" +"```" +msgstr "" +"```rust\n" +"let second = self.amounts.read()[1];\n" +"```" + #: src/ch02/list.md:36 msgid "" "Note that unlike `get`, using this bracket notation panics when accessing an " @@ -2750,8 +6650,7 @@ msgstr "" "(崩溃)。" #: src/ch02/list.md:38 -#, fuzzy -msgid "Support for custom types" +msgid "### Support for custom types" msgstr "### 支持自定义类型" #: src/ch02/list.md:40 @@ -2761,13 +6660,12 @@ msgid "" "trait. You can have the compiler derive it for you using the " "`#[derive(starknet::Store)]` attribute." msgstr "" -"`List` 默认支持大多数 corelib 类型。如果您想在 `List` 中存储自己的自定义类" -"型,该类型必须实现 `Store` 特性。您可以使用 `#[derive(starknet::Store)]` 属性" -"让编译器自动生成。" +"`List` 默认支持大多数 corelib 类型。如果您想在 `List` 中存储自己的自定义类型," +"该类型必须实现 `Store` 特性。您可以使用 `#[derive(starknet::Store)]` 属性让编" +"译器自动生成。" #: src/ch02/list.md:42 -#, fuzzy -msgid "Caveats" +msgid "### Caveats" msgstr "### 注意事项" #: src/ch02/list.md:44 @@ -2776,34 +6674,45 @@ msgstr "在使用 `List` 时,有两个特点应该注意:" #: src/ch02/list.md:46 msgid "" -"The `append` operation costs 2 storage writes - one for the value itself and " -"another one for updating the List's length" +"1. The `append` operation costs 2 storage writes - one for the value itself " +"and another one for updating the List's length\n" +"2. Due to a compiler limitation, it is not possible to use mutating " +"operations with a single inline statement. For example, `self.amounts.read()." +"append(42);` will not work. You have to do it in 2 steps:" msgstr "" +"1. `append` 操作消耗 2 次存储写入操作 - 一次是为了值本身,另一次是为了更新列表" +"的长度。\n" +"2. 由于编译器的限制,不能使用单个内联语句进行变更操作。例如,`self.amounts." +"read().append(42);` 是不行的。你必须分两步进行:" -#: src/ch02/list.md:47 -#, fuzzy +#: src/ch02/list.md:49 msgid "" -"Due to a compiler limitation, it is not possible to use mutating operations " -"with a single inline statement. For example, `self.amounts.read().append(42);" -"` will not work. You have to do it in 2 steps:" +"```rust\n" +"let mut amounts = self.amounts.read();\n" +"amounts.append(42);\n" +"```" msgstr "" -"1. `append` 操作消耗 2 次存储写入操作 - 一次是为了值本身,另一次是为了更新列" -"表的长度。\n" -"2. 由于编译器的限制,不能使用单个内联语句进行变更操作。例如,`self.amounts." -"read().append(42);` 是不行的。你必须分两步进行:" +"```rust\n" +"let mut amounts = self.amounts.read();\n" +"amounts.append(42);\n" +"```" #: src/ch02/list.md:54 -#, fuzzy -msgid "Dependencies" +msgid "### Dependencies" msgstr "### 依赖关系" #: src/ch02/list.md:56 msgid "Update your project dependencies by in the `Scarb.toml` file:" msgstr "在 `Scarb.toml` 里更新您项目的依赖:" -#: src/ch02/list.md:60 -#, fuzzy -msgid "\"https://github.com/keep-starknet-strange/alexandria.git\"" +#: src/ch02/list.md:57 +msgid "" +"```rust\n" +"[dependencies]\n" +"(...)\n" +"alexandria_storage = { git = \"https://github.com/keep-starknet-strange/" +"alexandria.git\" }\n" +"```" msgstr "" "```rust\n" "[dependencies]\n" @@ -2818,4784 +6727,166 @@ msgid "" "amounts and tasks:" msgstr "例如,我们用 `List` 来创建一个跟踪`amount`和`tasks`的合约:" -#~ msgid "" -#~ "- [Starklings](https://github.com/shramee/starklings-cairo1): An " -#~ "interactive tutorial to get you up and running with Cairo v1 and " -#~ "Starknet \n" -#~ "- [Cairopractice](https://cairopractice.com/): A blog with a series of " -#~ "articles about Cairo and Starknet\n" -#~ "- [Cairo by example](https://cairo-by-example.com/): An introduction to " -#~ "Cairo, with simple examples" -#~ msgstr "" -#~ "- [Starklings](https://github.com/shramee/starklings-cairo1):让您使用 " -#~ "Cairo v1 和Starknet互动的教程\n" -#~ "- [Cairopractice](https://cairopractice.com/):关于Cairo和Starknet的一系列" -#~ "文章的博客\n" -#~ "- [Cairo by example](https://cairo-by-example.com/):Cairo 简介,附带简单" -#~ "示例" - -#~ msgid "
Last change: 2023-11-30
" -#~ msgstr "
Last change: 2023-11-30
" - -#~ msgid "
Last change: 2023-10-12
" -#~ msgstr "
Last change: 2023-10-12
" - -#~ msgid "# Storage" -#~ msgstr "# 存储" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::contract]\n" -#~ "mod Contract {\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::contract]\n" -#~ "mod Contract {\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::contract]\n" -#~ "mod Contract {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " a: u128,\n" -#~ " b: u8,\n" -#~ " c: u256\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::contract]\n" -#~ "mod Contract {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " a: u128,\n" -#~ " b: u8,\n" -#~ " c: u256\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "
Last change: 2023-11-20
" -#~ msgstr "
Last change: 2023-11-20
" - -#~ msgid "# Constructor" -#~ msgstr "# 构造函数" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::contract]\n" -#~ "mod ExampleConstructor {\n" -#~ " use starknet::ContractAddress;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " names: LegacyMap::,\n" -#~ " }\n" -#~ "\n" -#~ " // The constructor is decorated with a `#[constructor]` attribute.\n" -#~ " // It is not inside an `impl` block.\n" -#~ " #[constructor]\n" -#~ " fn constructor(ref self: ContractState, name: felt252, address: " -#~ "ContractAddress) {\n" -#~ " self.names.write(address, name);\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::contract]\n" -#~ "mod ExampleConstructor {\n" -#~ " use starknet::ContractAddress;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " names: LegacyMap::,\n" -#~ " }\n" -#~ "\n" -#~ " // The constructor is decorated with a `#[constructor]` attribute.\n" -#~ " // It is not inside an `impl` block.\n" -#~ " #[constructor]\n" -#~ " fn constructor(ref self: ContractState, name: felt252, address: " -#~ "ContractAddress) {\n" -#~ " self.names.write(address, name);\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Variables" -#~ msgstr "# 变量" - -#~ msgid "" -#~ "- Local\n" -#~ " - declared inside a function\n" -#~ " - not stored on the blockchain\n" -#~ "- Storage\n" -#~ " - declared in the [Storage](./storage.md) of a contract\n" -#~ " - can be accessed from one execution to another\n" -#~ "- Global\n" -#~ " - provides information about the blockchain\n" -#~ " - accessed anywhere, even within library functions" -#~ msgstr "" -#~ "- 局部\n" -#~ " - 在函数中声明\n" -#~ " - 不存储在区块链中\n" -#~ "- 存储\n" -#~ " - 在合约的 [Storage](./storage.md) 中声明\n" -#~ " - 可从一个执行过程访问到另一个执行过程\n" -#~ "- 全局\n" -#~ " - 提供有关区块链的信息\n" -#~ " - 可在任何地方访问,甚至在库函数中" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait ILocalVariablesExample {\n" -#~ " fn do_something(self: @TContractState, value: u32) -> u32;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod LocalVariablesExample {\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl LocalVariablesExample of super::" -#~ "ILocalVariablesExample {\n" -#~ " fn do_something(self: @ContractState, value: u32) -> u32 {\n" -#~ " // This variable is local to the current block. It can't be " -#~ "accessed once it goes out of scope.\n" -#~ " let increment = 10;\n" -#~ "\n" -#~ " {\n" -#~ " // The scope of a code block allows for local variable " -#~ "declaration\n" -#~ " // We can access variables defined in higher scopes.\n" -#~ " let sum = value + increment;\n" -#~ " sum\n" -#~ " }\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait ILocalVariablesExample {\n" -#~ " fn do_something(self: @TContractState, value: u32) -> u32;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod LocalVariablesExample {\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl LocalVariablesExample of super::" -#~ "ILocalVariablesExample {\n" -#~ " fn do_something(self: @ContractState, value: u32) -> u32 {\n" -#~ " // This variable is local to the current block. It can't be " -#~ "accessed once it goes out of scope.\n" -#~ " let increment = 10;\n" -#~ "\n" -#~ " {\n" -#~ " // The scope of a code block allows for local variable " -#~ "declaration\n" -#~ " // We can access variables defined in higher scopes.\n" -#~ " let sum = value + increment;\n" -#~ " sum\n" -#~ " }\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IStorageVariableExample {\n" -#~ " fn set(ref self: TContractState, value: u32);\n" -#~ " fn get(self: @TContractState) -> u32;\n" -#~ "}\n" -#~ "#[starknet::contract]\n" -#~ "mod StorageVariablesExample {\n" -#~ " // All storage variables are contained in a struct called Storage\n" -#~ " // annotated with the `#[storage]` attribute\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " // Storage variable holding a number\n" -#~ " value: u32\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl StorageVariablesExample of super::" -#~ "IStorageVariableExample {\n" -#~ " // Write to storage variables by sending a transaction that calls " -#~ "an external function\n" -#~ " fn set(ref self: ContractState, value: u32) {\n" -#~ " self.value.write(value);\n" -#~ " }\n" -#~ "\n" -#~ " // Read from storage variables without sending transactions\n" -#~ " fn get(self: @ContractState) -> u32 {\n" -#~ " self.value.read()\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IStorageVariableExample {\n" -#~ " fn set(ref self: TContractState, value: u32);\n" -#~ " fn get(self: @TContractState) -> u32;\n" -#~ "}\n" -#~ "#[starknet::contract]\n" -#~ "mod StorageVariablesExample {\n" -#~ " // All storage variables are contained in a struct called Storage\n" -#~ " // annotated with the `#[storage]` attribute\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " // Storage variable holding a number\n" -#~ " value: u32\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl StorageVariablesExample of super::" -#~ "IStorageVariableExample {\n" -#~ " // Write to storage variables by sending a transaction that calls " -#~ "an external function\n" -#~ " fn set(ref self: ContractState, value: u32) {\n" -#~ " self.value.write(value);\n" -#~ " }\n" -#~ "\n" -#~ " // Read from storage variables without sending transactions\n" -#~ " fn get(self: @ContractState) -> u32 {\n" -#~ " self.value.read()\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IGlobalExample {\n" -#~ " fn foo(ref self: TContractState);\n" -#~ "}\n" -#~ "#[starknet::contract]\n" -#~ "mod GlobalExample {\n" -#~ " // import the required functions from the starknet core library\n" -#~ " use starknet::get_caller_address;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl GlobalExampleImpl of super::IGlobalExample {\n" -#~ " fn foo(ref self: ContractState) {\n" -#~ " // Call the get_caller_address function to get the sender " -#~ "address\n" -#~ " let caller = get_caller_address();\n" -#~ " // ...\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IGlobalExample {\n" -#~ " fn foo(ref self: TContractState);\n" -#~ "}\n" -#~ "#[starknet::contract]\n" -#~ "mod GlobalExample {\n" -#~ " // import the required functions from the starknet core library\n" -#~ " use starknet::get_caller_address;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl GlobalExampleImpl of super::IGlobalExample {\n" -#~ " fn foo(ref self: ContractState) {\n" -#~ " // Call the get_caller_address function to get the sender " -#~ "address\n" -#~ " let caller = get_caller_address();\n" -#~ " // ...\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Visibility and Mutability" -#~ msgstr "# 可见性和可变性" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IExampleContract {\n" -#~ " fn set(ref self: TContractState, value: u32);\n" -#~ " fn get(self: @TContractState) -> u32;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod ExampleContract {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " value: u32\n" -#~ " }\n" -#~ "\n" -#~ "\n" -#~ " // The `abi(embed_v0)` attribute indicates that all the functions in " -#~ "this implementation can be called externally.\n" -#~ " // Omitting this attribute would make all the functions in this " -#~ "implementation internal.\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ExampleContract of super::IExampleContract {\n" -#~ " // The `set` function can be called externally because it is " -#~ "written inside an implementation marked as `#[external]`.\n" -#~ " // It can modify the contract's state as it is passed as a " -#~ "reference.\n" -#~ " fn set(ref self: ContractState, value: u32) {\n" -#~ " self.value.write(value);\n" -#~ " }\n" -#~ "\n" -#~ " // The `get` function can be called externally because it is " -#~ "written inside an implementation marked as `#[external]`.\n" -#~ " // However, it can't modify the contract's state is passed as a " -#~ "snapshot: it is only a \"view\" function.\n" -#~ " fn get(self: @ContractState) -> u32 {\n" -#~ " // We can call an internal function from any functions within " -#~ "the contract\n" -#~ " PrivateFunctionsTrait::_read_value(self)\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " // The lack of the `external` attribute indicates that all the " -#~ "functions in this implementation can only be called internally.\n" -#~ " // We name the trait `PrivateFunctionsTrait` to indicate that it is " -#~ "an internal trait allowing us to call internal functions.\n" -#~ " #[generate_trait]\n" -#~ " impl PrivateFunctions of PrivateFunctionsTrait {\n" -#~ " // The `_read_value` function is outside the implementation that " -#~ "is marked as `#[abi(embed_v0)]`, so it's an _internal_ function\n" -#~ " // and can only be called from within the contract.\n" -#~ " // However, it can't modify the contract's state is passed as a " -#~ "snapshot: it is only a \"view\" function.\n" -#~ " fn _read_value(self: @ContractState) -> u32 {\n" -#~ " self.value.read()\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IExampleContract {\n" -#~ " fn set(ref self: TContractState, value: u32);\n" -#~ " fn get(self: @TContractState) -> u32;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod ExampleContract {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " value: u32\n" -#~ " }\n" -#~ "\n" -#~ "\n" -#~ " // The `abi(embed_v0)` attribute indicates that all the functions in " -#~ "this implementation can be called externally.\n" -#~ " // Omitting this attribute would make all the functions in this " -#~ "implementation internal.\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ExampleContract of super::IExampleContract {\n" -#~ " // The `set` function can be called externally because it is " -#~ "written inside an implementation marked as `#[external]`.\n" -#~ " // It can modify the contract's state as it is passed as a " -#~ "reference.\n" -#~ " fn set(ref self: ContractState, value: u32) {\n" -#~ " self.value.write(value);\n" -#~ " }\n" -#~ "\n" -#~ " // The `get` function can be called externally because it is " -#~ "written inside an implementation marked as `#[external]`.\n" -#~ " // However, it can't modify the contract's state is passed as a " -#~ "snapshot: it is only a \"view\" function.\n" -#~ " fn get(self: @ContractState) -> u32 {\n" -#~ " // We can call an internal function from any functions within " -#~ "the contract\n" -#~ " PrivateFunctionsTrait::_read_value(self)\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " // The lack of the `external` attribute indicates that all the " -#~ "functions in this implementation can only be called internally.\n" -#~ " // We name the trait `PrivateFunctionsTrait` to indicate that it is " -#~ "an internal trait allowing us to call internal functions.\n" -#~ " #[generate_trait]\n" -#~ " impl PrivateFunctions of PrivateFunctionsTrait {\n" -#~ " // The `_read_value` function is outside the implementation that " -#~ "is marked as `#[abi(embed_v0)]`, so it's an _internal_ function\n" -#~ " // and can only be called from within the contract.\n" -#~ " // However, it can't modify the contract's state is passed as a " -#~ "snapshot: it is only a \"view\" function.\n" -#~ " fn _read_value(self: @ContractState) -> u32 {\n" -#~ " self.value.read()\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "
Last change: 2023-10-19
" -#~ msgstr "
Last change: 2023-10-19
" - -#~ msgid "" -#~ "- The contract has a state variable called 'counter' that is initialized " -#~ "to 0.\n" -#~ "\n" -#~ "- When a user calls 'increment', the contract increments the counter by " -#~ "1.\n" -#~ "\n" -#~ "- When a user calls 'decrement', the contract decrements the counter by 1." -#~ msgstr "" -#~ "- 合约有一个名为 'counter'的状态变量,初始化为 0。\n" -#~ "\n" -#~ "- 当用户调用 'increment'时,合约会将计数器递增 1。\n" -#~ "\n" -#~ "- 当用户调用 'decrement'时,合约会将计数器递减 1。" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait ISimpleCounter {\n" -#~ " fn get_current_count(self: @TContractState) -> u128;\n" -#~ " fn increment(ref self: TContractState);\n" -#~ " fn decrement(ref self: TContractState);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod SimpleCounter {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " // Counter variable\n" -#~ " counter: u128,\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(ref self: ContractState, init_value: u128) {\n" -#~ " // Store initial value\n" -#~ " self.counter.write(init_value);\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl SimpleCounter of super::ISimpleCounter {\n" -#~ " fn get_current_count(self: @ContractState) -> u128 {\n" -#~ " return self.counter.read();\n" -#~ " }\n" -#~ "\n" -#~ " fn increment(ref self: ContractState) {\n" -#~ " // Store counter value + 1\n" -#~ " let counter = self.counter.read() + 1;\n" -#~ " self.counter.write(counter);\n" -#~ " }\n" -#~ " fn decrement(ref self: ContractState) {\n" -#~ " // Store counter value - 1\n" -#~ " let counter = self.counter.read() - 1;\n" -#~ " self.counter.write(counter);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait ISimpleCounter {\n" -#~ " fn get_current_count(self: @TContractState) -> u128;\n" -#~ " fn increment(ref self: TContractState);\n" -#~ " fn decrement(ref self: TContractState);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod SimpleCounter {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " // Counter variable\n" -#~ " counter: u128,\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(ref self: ContractState, init_value: u128) {\n" -#~ " // Store initial value\n" -#~ " self.counter.write(init_value);\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl SimpleCounter of super::ISimpleCounter {\n" -#~ " fn get_current_count(self: @ContractState) -> u128 {\n" -#~ " return self.counter.read();\n" -#~ " }\n" -#~ "\n" -#~ " fn increment(ref self: ContractState) {\n" -#~ " // Store counter value + 1\n" -#~ " let counter = self.counter.read() + 1;\n" -#~ " self.counter.write(counter);\n" -#~ " }\n" -#~ " fn decrement(ref self: ContractState) {\n" -#~ " // Store counter value - 1\n" -#~ " let counter = self.counter.read() - 1;\n" -#~ " self.counter.write(counter);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "
Last change: 2023-11-04
" -#~ msgstr "
Last change: 2023-11-04
" - -#~ msgid "# Mappings" -#~ msgstr "# 映射" - -#~ msgid "" -#~ "```rust\n" -#~ "use starknet::ContractAddress;\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IMapContract {\n" -#~ " fn set(ref self: TContractState, key: ContractAddress, value: " -#~ "felt252);\n" -#~ " fn get(self: @TContractState, key: ContractAddress) -> felt252;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod MapContract {\n" -#~ " use starknet::ContractAddress;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " // The `LegacyMap` type is only available inside the `Storage` " -#~ "struct.\n" -#~ " map: LegacyMap::,\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl MapContractImpl of super::IMapContract {\n" -#~ " fn set(ref self: ContractState, key: ContractAddress, value: " -#~ "felt252) {\n" -#~ " self.map.write(key, value);\n" -#~ " }\n" -#~ "\n" -#~ " fn get(self: @ContractState, key: ContractAddress) -> felt252 {\n" -#~ " self.map.read(key)\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "use starknet::ContractAddress;\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IMapContract {\n" -#~ " fn set(ref self: TContractState, key: ContractAddress, value: " -#~ "felt252);\n" -#~ " fn get(self: @TContractState, key: ContractAddress) -> felt252;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod MapContract {\n" -#~ " use starknet::ContractAddress;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " // The `LegacyMap` type is only available inside the `Storage` " -#~ "struct.\n" -#~ " map: LegacyMap::,\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl MapContractImpl of super::IMapContract {\n" -#~ " fn set(ref self: ContractState, key: ContractAddress, value: " -#~ "felt252) {\n" -#~ " self.map.write(key, value);\n" -#~ " }\n" -#~ "\n" -#~ " fn get(self: @ContractState, key: ContractAddress) -> felt252 {\n" -#~ " self.map.read(key)\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Errors" -#~ msgstr "# 错误" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IErrorsExample {\n" -#~ " fn test_assert(self: @TContractState, i: u256);\n" -#~ " fn test_panic(self: @TContractState, i: u256);\n" -#~ "}\n" -#~ "#[starknet::contract]\n" -#~ "mod ErrorsExample {\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ErrorsExample of super::IErrorsExample {\n" -#~ " fn test_assert(self: @ContractState, i: u256) {\n" -#~ " // Assert used to validate a condition\n" -#~ " // and abort execution if the condition is not met\n" -#~ " assert(i > 0, 'i must be greater than 0');\n" -#~ " }\n" -#~ "\n" -#~ " fn test_panic(self: @ContractState, i: u256) {\n" -#~ " if (i == 0) {\n" -#~ " // Panic used to abort execution directly\n" -#~ " panic_with_felt252('i must not be 0');\n" -#~ " }\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IErrorsExample {\n" -#~ " fn test_assert(self: @TContractState, i: u256);\n" -#~ " fn test_panic(self: @TContractState, i: u256);\n" -#~ "}\n" -#~ "#[starknet::contract]\n" -#~ "mod ErrorsExample {\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ErrorsExample of super::IErrorsExample {\n" -#~ " fn test_assert(self: @ContractState, i: u256) {\n" -#~ " // Assert used to validate a condition\n" -#~ " // and abort execution if the condition is not met\n" -#~ " assert(i > 0, 'i must be greater than 0');\n" -#~ " }\n" -#~ "\n" -#~ " fn test_panic(self: @ContractState, i: u256) {\n" -#~ " if (i == 0) {\n" -#~ " // Panic used to abort execution directly\n" -#~ " panic_with_felt252('i must not be 0');\n" -#~ " }\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "mod Errors {\n" -#~ " const NOT_POSITIVE: felt252 = 'must be greater than 0';\n" -#~ " const NOT_NULL: felt252 = 'must not be null';\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait ICustomErrorsExample {\n" -#~ " fn test_assert(self: @TContractState, i: u256);\n" -#~ " fn test_panic(self: @TContractState, i: u256);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod CustomErrorsExample {\n" -#~ " use super::Errors;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl CustomErrorsExample of super::" -#~ "ICustomErrorsExample {\n" -#~ " fn test_assert(self: @ContractState, i: u256) {\n" -#~ " assert(i > 0, Errors::NOT_POSITIVE);\n" -#~ " }\n" -#~ "\n" -#~ " fn test_panic(self: @ContractState, i: u256) {\n" -#~ " if (i == 0) {\n" -#~ " panic_with_felt252(Errors::NOT_NULL);\n" -#~ " }\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "mod Errors {\n" -#~ " const NOT_POSITIVE: felt252 = 'must be greater than 0';\n" -#~ " const NOT_NULL: felt252 = 'must not be null';\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait ICustomErrorsExample {\n" -#~ " fn test_assert(self: @TContractState, i: u256);\n" -#~ " fn test_panic(self: @TContractState, i: u256);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod CustomErrorsExample {\n" -#~ " use super::Errors;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl CustomErrorsExample of super::" -#~ "ICustomErrorsExample {\n" -#~ " fn test_assert(self: @ContractState, i: u256) {\n" -#~ " assert(i > 0, Errors::NOT_POSITIVE);\n" -#~ " }\n" -#~ "\n" -#~ " fn test_panic(self: @ContractState, i: u256) {\n" -#~ " if (i == 0) {\n" -#~ " panic_with_felt252(Errors::NOT_NULL);\n" -#~ " }\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "mod VaultErrors {\n" -#~ " const INSUFFICIENT_BALANCE: felt252 = 'insufficient_balance';\n" -#~ "// you can define more errors here\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IVaultErrorsExample {\n" -#~ " fn deposit(ref self: TContractState, amount: u256);\n" -#~ " fn withdraw(ref self: TContractState, amount: u256);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod VaultErrorsExample {\n" -#~ " use super::VaultErrors;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " balance: u256,\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl VaultErrorsExample of super::IVaultErrorsExample " -#~ "{\n" -#~ " fn deposit(ref self: ContractState, amount: u256) {\n" -#~ " let mut balance = self.balance.read();\n" -#~ " balance = balance + amount;\n" -#~ " self.balance.write(balance);\n" -#~ " }\n" -#~ "\n" -#~ " fn withdraw(ref self: ContractState, amount: u256) {\n" -#~ " let mut balance = self.balance.read();\n" -#~ "\n" -#~ " assert(balance >= amount, VaultErrors::" -#~ "INSUFFICIENT_BALANCE);\n" -#~ "\n" -#~ " // Or using panic:\n" -#~ " if (balance >= amount) {\n" -#~ " panic_with_felt252(VaultErrors::INSUFFICIENT_BALANCE);\n" -#~ " }\n" -#~ "\n" -#~ " let balance = balance - amount;\n" -#~ "\n" -#~ " self.balance.write(balance);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "mod VaultErrors {\n" -#~ " const INSUFFICIENT_BALANCE: felt252 = 'insufficient_balance';\n" -#~ "// you can define more errors here\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IVaultErrorsExample {\n" -#~ " fn deposit(ref self: TContractState, amount: u256);\n" -#~ " fn withdraw(ref self: TContractState, amount: u256);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod VaultErrorsExample {\n" -#~ " use super::VaultErrors;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " balance: u256,\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl VaultErrorsExample of super::IVaultErrorsExample " -#~ "{\n" -#~ " fn deposit(ref self: ContractState, amount: u256) {\n" -#~ " let mut balance = self.balance.read();\n" -#~ " balance = balance + amount;\n" -#~ " self.balance.write(balance);\n" -#~ " }\n" -#~ "\n" -#~ " fn withdraw(ref self: ContractState, amount: u256) {\n" -#~ " let mut balance = self.balance.read();\n" -#~ "\n" -#~ " assert(balance >= amount, VaultErrors::" -#~ "INSUFFICIENT_BALANCE);\n" -#~ "\n" -#~ " // Or using panic:\n" -#~ " if (balance >= amount) {\n" -#~ " panic_with_felt252(VaultErrors::INSUFFICIENT_BALANCE);\n" -#~ " }\n" -#~ "\n" -#~ " let balance = balance - amount;\n" -#~ "\n" -#~ " self.balance.write(balance);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Events" -#~ msgstr "# 事件" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IEventCounter {\n" -#~ " fn increment(ref self: TContractState);\n" -#~ "}\n" -#~ "#[starknet::contract]\n" -#~ "mod EventCounter {\n" -#~ " use starknet::{get_caller_address, ContractAddress};\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " // Counter value\n" -#~ " counter: u128,\n" -#~ " }\n" -#~ "\n" -#~ " #[event]\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " // The event enum must be annotated with the `#[event]` attribute.\n" -#~ " // It must also derive the `Drop` and `starknet::Event` traits.\n" -#~ " enum Event {\n" -#~ " CounterIncreased: CounterIncreased,\n" -#~ " UserIncreaseCounter: UserIncreaseCounter\n" -#~ " }\n" -#~ "\n" -#~ " // By deriving the `starknet::Event` trait, we indicate to the " -#~ "compiler that\n" -#~ " // this struct will be used when emitting events.\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " struct CounterIncreased {\n" -#~ " amount: u128\n" -#~ " }\n" -#~ "\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " struct UserIncreaseCounter {\n" -#~ " // The `#[key]` attribute indicates that this event will be " -#~ "indexed.\n" -#~ " #[key]\n" -#~ " user: ContractAddress,\n" -#~ " new_value: u128,\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl EventCounter of super::IEventCounter {\n" -#~ " fn increment(ref self: ContractState) {\n" -#~ " let mut counter = self.counter.read();\n" -#~ " counter += 1;\n" -#~ " self.counter.write(counter);\n" -#~ " // Emit event\n" -#~ " self.emit(Event::CounterIncreased(CounterIncreased { amount: " -#~ "1 }));\n" -#~ " self\n" -#~ " .emit(\n" -#~ " Event::UserIncreaseCounter(\n" -#~ " UserIncreaseCounter {\n" -#~ " user: get_caller_address(), new_value: self." -#~ "counter.read()\n" -#~ " }\n" -#~ " )\n" -#~ " );\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IEventCounter {\n" -#~ " fn increment(ref self: TContractState);\n" -#~ "}\n" -#~ "#[starknet::contract]\n" -#~ "mod EventCounter {\n" -#~ " use starknet::{get_caller_address, ContractAddress};\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " // Counter value\n" -#~ " counter: u128,\n" -#~ " }\n" -#~ "\n" -#~ " #[event]\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " // The event enum must be annotated with the `#[event]` attribute.\n" -#~ " // It must also derive the `Drop` and `starknet::Event` traits.\n" -#~ " enum Event {\n" -#~ " CounterIncreased: CounterIncreased,\n" -#~ " UserIncreaseCounter: UserIncreaseCounter\n" -#~ " }\n" -#~ "\n" -#~ " // By deriving the `starknet::Event` trait, we indicate to the " -#~ "compiler that\n" -#~ " // this struct will be used when emitting events.\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " struct CounterIncreased {\n" -#~ " amount: u128\n" -#~ " }\n" -#~ "\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " struct UserIncreaseCounter {\n" -#~ " // The `#[key]` attribute indicates that this event will be " -#~ "indexed.\n" -#~ " #[key]\n" -#~ " user: ContractAddress,\n" -#~ " new_value: u128,\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl EventCounter of super::IEventCounter {\n" -#~ " fn increment(ref self: ContractState) {\n" -#~ " let mut counter = self.counter.read();\n" -#~ " counter += 1;\n" -#~ " self.counter.write(counter);\n" -#~ " // Emit event\n" -#~ " self.emit(Event::CounterIncreased(CounterIncreased { amount: " -#~ "1 }));\n" -#~ " self\n" -#~ " .emit(\n" -#~ " Event::UserIncreaseCounter(\n" -#~ " UserIncreaseCounter {\n" -#~ " user: get_caller_address(), new_value: self." -#~ "counter.read()\n" -#~ " }\n" -#~ " )\n" -#~ " );\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Storing Custom Types" -#~ msgstr "# 存储自定义类型" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IStoringCustomType {\n" -#~ " fn set_person(ref self: TContractState, person: Person);\n" -#~ "}\n" -#~ "\n" -#~ "// Deriving the starknet::Store trait\n" -#~ "// allows us to store the `Person` struct in the contract's storage.\n" -#~ "#[derive(Drop, Serde, Copy, starknet::Store)]\n" -#~ "struct Person {\n" -#~ " age: u8,\n" -#~ " name: felt252\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod StoringCustomType {\n" -#~ " use super::Person;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " person: Person\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl StoringCustomType of super::IStoringCustomType {\n" -#~ " fn set_person(ref self: ContractState, person: Person) {\n" -#~ " self.person.write(person);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IStoringCustomType {\n" -#~ " fn set_person(ref self: TContractState, person: Person);\n" -#~ "}\n" -#~ "\n" -#~ "// Deriving the starknet::Store trait\n" -#~ "// allows us to store the `Person` struct in the contract's storage.\n" -#~ "#[derive(Drop, Serde, Copy, starknet::Store)]\n" -#~ "struct Person {\n" -#~ " age: u8,\n" -#~ " name: felt252\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod StoringCustomType {\n" -#~ " use super::Person;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " person: Person\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl StoringCustomType of super::IStoringCustomType {\n" -#~ " fn set_person(ref self: ContractState, person: Person) {\n" -#~ " self.person.write(person);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Custom types in entrypoints" -#~ msgstr "# 入口点中的自定义类型" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait ISerdeCustomType {\n" -#~ " fn person_input(ref self: TContractState, person: SerdeCustomType::" -#~ "Person);\n" -#~ " fn person_output(self: @TContractState) -> SerdeCustomType::Person;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod SerdeCustomType {\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " // Deriving the `Serde` trait allows us to use\n" -#~ " // the Person type as an entrypoint parameter and return value\n" -#~ " #[derive(Drop, Serde)]\n" -#~ " struct Person {\n" -#~ " age: u8,\n" -#~ " name: felt252\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl SerdeCustomType of super::ISerdeCustomType {\n" -#~ " fn person_input(ref self: ContractState, person: Person) {}\n" -#~ "\n" -#~ " fn person_output(self: @ContractState) -> Person {\n" -#~ " Person { age: 10, name: 'Joe' }\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait ISerdeCustomType {\n" -#~ " fn person_input(ref self: TContractState, person: SerdeCustomType::" -#~ "Person);\n" -#~ " fn person_output(self: @TContractState) -> SerdeCustomType::Person;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod SerdeCustomType {\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " // Deriving the `Serde` trait allows us to use\n" -#~ " // the Person type as an entrypoint parameter and return value\n" -#~ " #[derive(Drop, Serde)]\n" -#~ " struct Person {\n" -#~ " age: u8,\n" -#~ " name: felt252\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl SerdeCustomType of super::ISerdeCustomType {\n" -#~ " fn person_input(ref self: ContractState, person: Person) {}\n" -#~ "\n" -#~ " fn person_output(self: @ContractState) -> Person {\n" -#~ " Person { age: 10, name: 'Joe' }\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Documentation" -#~ msgstr "# 文档" - -#~ msgid "
Last change: 2023-12-05
" -#~ msgstr "
Last change: 2023-12-05
" - -#~ msgid "# Deploy and interact with contracts" -#~ msgstr "# 部署合约并与合约交互" - -#~ msgid "# Contract interfaces and Traits generation" -#~ msgstr "# 合约接口和Trait生成" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IExplicitInterfaceContract {\n" -#~ " fn get_value(self: @TContractState) -> u32;\n" -#~ " fn set_value(ref self: TContractState, value: u32);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod ExplicitInterfaceContract {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " value: u32\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ExplicitInterfaceContract of super::" -#~ "IExplicitInterfaceContract {\n" -#~ " fn get_value(self: @ContractState) -> u32 {\n" -#~ " self.value.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn set_value(ref self: ContractState, value: u32) {\n" -#~ " self.value.write(value);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IExplicitInterfaceContract {\n" -#~ " fn get_value(self: @TContractState) -> u32;\n" -#~ " fn set_value(ref self: TContractState, value: u32);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod ExplicitInterfaceContract {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " value: u32\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ExplicitInterfaceContract of super::" -#~ "IExplicitInterfaceContract {\n" -#~ " fn get_value(self: @ContractState) -> u32 {\n" -#~ " self.value.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn set_value(ref self: ContractState, value: u32) {\n" -#~ " self.value.write(value);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::contract]\n" -#~ "mod ImplicitInterfaceContract {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " value: u32\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(per_item)]\n" -#~ " #[generate_trait]\n" -#~ " impl ImplicitInterfaceContract of IImplicitInterfaceContract {\n" -#~ " #[external(v0)]\n" -#~ " fn get_value(self: @ContractState) -> u32 {\n" -#~ " self.value.read()\n" -#~ " }\n" -#~ "\n" -#~ " #[external(v0)]\n" -#~ " fn set_value(ref self: ContractState, value: u32) {\n" -#~ " self.value.write(value);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::contract]\n" -#~ "mod ImplicitInterfaceContract {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " value: u32\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(per_item)]\n" -#~ " #[generate_trait]\n" -#~ " impl ImplicitInterfaceContract of IImplicitInterfaceContract {\n" -#~ " #[external(v0)]\n" -#~ " fn get_value(self: @ContractState) -> u32 {\n" -#~ " self.value.read()\n" -#~ " }\n" -#~ "\n" -#~ " #[external(v0)]\n" -#~ " fn set_value(ref self: ContractState, value: u32) {\n" -#~ " self.value.write(value);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IImplicitInternalContract {\n" -#~ " fn add(ref self: TContractState, nb: u32);\n" -#~ " fn get_value(self: @TContractState) -> u32;\n" -#~ " fn get_const(self: @TContractState) -> u32;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod ImplicitInternalContract {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " value: u32\n" -#~ " }\n" -#~ "\n" -#~ " #[generate_trait]\n" -#~ " impl InternalFunctions of InternalFunctionsTrait {\n" -#~ " fn set_value(ref self: ContractState, value: u32) {\n" -#~ " self.value.write(value);\n" -#~ " }\n" -#~ " fn get_const() -> u32 {\n" -#~ " 42\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(ref self: ContractState) {\n" -#~ " self.set_value(0);\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ImplicitInternalContract of super::" -#~ "IImplicitInternalContract {\n" -#~ " fn add(ref self: ContractState, nb: u32) {\n" -#~ " self.set_value(self.value.read() + nb);\n" -#~ " }\n" -#~ " fn get_value(self: @ContractState) -> u32 {\n" -#~ " self.value.read()\n" -#~ " }\n" -#~ " fn get_const(self: @ContractState) -> u32 {\n" -#~ " self.get_const()\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IImplicitInternalContract {\n" -#~ " fn add(ref self: TContractState, nb: u32);\n" -#~ " fn get_value(self: @TContractState) -> u32;\n" -#~ " fn get_const(self: @TContractState) -> u32;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod ImplicitInternalContract {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " value: u32\n" -#~ " }\n" -#~ "\n" -#~ " #[generate_trait]\n" -#~ " impl InternalFunctions of InternalFunctionsTrait {\n" -#~ " fn set_value(ref self: ContractState, value: u32) {\n" -#~ " self.value.write(value);\n" -#~ " }\n" -#~ " fn get_const() -> u32 {\n" -#~ " 42\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(ref self: ContractState) {\n" -#~ " self.set_value(0);\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ImplicitInternalContract of super::" -#~ "IImplicitInternalContract {\n" -#~ " fn add(ref self: ContractState, nb: u32) {\n" -#~ " self.set_value(self.value.read() + nb);\n" -#~ " }\n" -#~ " fn get_value(self: @ContractState) -> u32 {\n" -#~ " self.value.read()\n" -#~ " }\n" -#~ " fn get_const(self: @ContractState) -> u32 {\n" -#~ " self.get_const()\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "
Last change: 2023-11-26
" -#~ msgstr "
Last change: 2023-11-26
" - -#~ msgid "# Calling other contracts" -#~ msgstr "# 调用其他合约" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait ICallee {\n" -#~ " fn set_value(ref self: TContractState, value: u128) -> u128;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod Callee {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " value: u128,\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ICalleeImpl of super::ICallee {\n" -#~ " fn set_value(ref self: ContractState, value: u128) -> u128 {\n" -#~ " self.value.write(value);\n" -#~ " value\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait ICallee {\n" -#~ " fn set_value(ref self: TContractState, value: u128) -> u128;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod Callee {\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " value: u128,\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ICalleeImpl of super::ICallee {\n" -#~ " fn set_value(ref self: ContractState, value: u128) -> u128 {\n" -#~ " self.value.write(value);\n" -#~ " value\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "use starknet::ContractAddress;\n" -#~ "\n" -#~ "// We need to have the interface of the callee contract defined\n" -#~ "// so that we can import the Dispatcher.\n" -#~ "#[starknet::interface]\n" -#~ "trait ICallee {\n" -#~ " fn set_value(ref self: TContractState, value: u128) -> u128;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait ICaller {\n" -#~ " fn set_value_from_address(ref self: TContractState, addr: " -#~ "ContractAddress, value: u128);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod Caller {\n" -#~ " // We import the Dispatcher of the called contract\n" -#~ " use super::{ICalleeDispatcher, ICalleeDispatcherTrait};\n" -#~ " use starknet::ContractAddress;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ICallerImpl of super::ICaller {\n" -#~ " fn set_value_from_address(ref self: ContractState, addr: " -#~ "ContractAddress, value: u128) {\n" -#~ " ICalleeDispatcher { contract_address: addr }." -#~ "set_value(value);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "use starknet::ContractAddress;\n" -#~ "\n" -#~ "// We need to have the interface of the callee contract defined\n" -#~ "// so that we can import the Dispatcher.\n" -#~ "#[starknet::interface]\n" -#~ "trait ICallee {\n" -#~ " fn set_value(ref self: TContractState, value: u128) -> u128;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait ICaller {\n" -#~ " fn set_value_from_address(ref self: TContractState, addr: " -#~ "ContractAddress, value: u128);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod Caller {\n" -#~ " // We import the Dispatcher of the called contract\n" -#~ " use super::{ICalleeDispatcher, ICalleeDispatcherTrait};\n" -#~ " use starknet::ContractAddress;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ICallerImpl of super::ICaller {\n" -#~ " fn set_value_from_address(ref self: ContractState, addr: " -#~ "ContractAddress, value: u128) {\n" -#~ " ICalleeDispatcher { contract_address: addr }." -#~ "set_value(value);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "use starknet::{ContractAddress, ClassHash};\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait ICounterFactory {\n" -#~ " /// Create a new counter contract from stored arguments\n" -#~ " fn create_counter(ref self: TContractState) -> ContractAddress;\n" -#~ "\n" -#~ " /// Create a new counter contract from the given arguments\n" -#~ " fn create_counter_at(ref self: TContractState, init_value: u128) -> " -#~ "ContractAddress;\n" -#~ "\n" -#~ " /// Update the argument\n" -#~ " fn update_init_value(ref self: TContractState, init_value: u128);\n" -#~ "\n" -#~ " /// Update the class hash of the Counter contract to deploy when " -#~ "creating a new counter\n" -#~ " fn update_counter_class_hash(ref self: TContractState, " -#~ "counter_class_hash: ClassHash);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod CounterFactory {\n" -#~ " use starknet::{ContractAddress, ClassHash};\n" -#~ " use starknet::syscalls::deploy_syscall;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " /// Store the constructor arguments of the contract to deploy\n" -#~ " init_value: u128,\n" -#~ " /// Store the class hash of the contract to deploy\n" -#~ " counter_class_hash: ClassHash,\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(ref self: ContractState, init_value: u128, class_hash: " -#~ "ClassHash) {\n" -#~ " self.init_value.write(init_value);\n" -#~ " self.counter_class_hash.write(class_hash);\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl Factory of super::ICounterFactory {\n" -#~ " fn create_counter_at(ref self: ContractState, init_value: u128) -" -#~ "> ContractAddress {\n" -#~ " // Contructor arguments\n" -#~ " let mut constructor_calldata: Array:: = array!" -#~ "[init_value.into()];\n" -#~ "\n" -#~ " // Contract deployment\n" -#~ " let (deployed_address, _) = deploy_syscall(\n" -#~ " self.counter_class_hash.read(), 0, constructor_calldata." -#~ "span(), false\n" -#~ " )\n" -#~ " .expect('failed to deploy counter');\n" -#~ "\n" -#~ " deployed_address\n" -#~ " }\n" -#~ "\n" -#~ " fn create_counter(ref self: ContractState) -> ContractAddress {\n" -#~ " self.create_counter_at(self.init_value.read())\n" -#~ " }\n" -#~ "\n" -#~ " fn update_init_value(ref self: ContractState, init_value: u128) " -#~ "{\n" -#~ " self.init_value.write(init_value);\n" -#~ " }\n" -#~ "\n" -#~ " fn update_counter_class_hash(ref self: ContractState, " -#~ "counter_class_hash: ClassHash) {\n" -#~ " self.counter_class_hash.write(counter_class_hash);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "use starknet::{ContractAddress, ClassHash};\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait ICounterFactory {\n" -#~ " /// Create a new counter contract from stored arguments\n" -#~ " fn create_counter(ref self: TContractState) -> ContractAddress;\n" -#~ "\n" -#~ " /// Create a new counter contract from the given arguments\n" -#~ " fn create_counter_at(ref self: TContractState, init_value: u128) -> " -#~ "ContractAddress;\n" -#~ "\n" -#~ " /// Update the argument\n" -#~ " fn update_init_value(ref self: TContractState, init_value: u128);\n" -#~ "\n" -#~ " /// Update the class hash of the Counter contract to deploy when " -#~ "creating a new counter\n" -#~ " fn update_counter_class_hash(ref self: TContractState, " -#~ "counter_class_hash: ClassHash);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod CounterFactory {\n" -#~ " use starknet::{ContractAddress, ClassHash};\n" -#~ " use starknet::syscalls::deploy_syscall;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " /// Store the constructor arguments of the contract to deploy\n" -#~ " init_value: u128,\n" -#~ " /// Store the class hash of the contract to deploy\n" -#~ " counter_class_hash: ClassHash,\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(ref self: ContractState, init_value: u128, class_hash: " -#~ "ClassHash) {\n" -#~ " self.init_value.write(init_value);\n" -#~ " self.counter_class_hash.write(class_hash);\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl Factory of super::ICounterFactory {\n" -#~ " fn create_counter_at(ref self: ContractState, init_value: u128) -" -#~ "> ContractAddress {\n" -#~ " // Contructor arguments\n" -#~ " let mut constructor_calldata: Array:: = array!" -#~ "[init_value.into()];\n" -#~ "\n" -#~ " // Contract deployment\n" -#~ " let (deployed_address, _) = deploy_syscall(\n" -#~ " self.counter_class_hash.read(), 0, constructor_calldata." -#~ "span(), false\n" -#~ " )\n" -#~ " .expect('failed to deploy counter');\n" -#~ "\n" -#~ " deployed_address\n" -#~ " }\n" -#~ "\n" -#~ " fn create_counter(ref self: ContractState) -> ContractAddress {\n" -#~ " self.create_counter_at(self.init_value.read())\n" -#~ " }\n" -#~ "\n" -#~ " fn update_init_value(ref self: ContractState, init_value: u128) " -#~ "{\n" -#~ " self.init_value.write(init_value);\n" -#~ " }\n" -#~ "\n" -#~ " fn update_counter_class_hash(ref self: ContractState, " -#~ "counter_class_hash: ClassHash) {\n" -#~ " self.counter_class_hash.write(counter_class_hash);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "" -#~ msgstr "" -#~ "" - -#~ msgid "" -#~ "\n" -#~ "\n" -#~ "
Last change: 2023-10-19
" -#~ msgstr "" -#~ "\n" -#~ "\n" -#~ "
Last change: 2023-10-19
" - -#~ msgid "" -#~ "```rust\n" -#~ "use starknet::ContractAddress;\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait ISimpleContract {\n" -#~ " fn get_value(self: @TContractState) -> u32;\n" -#~ " fn get_owner(self: @TContractState) -> ContractAddress;\n" -#~ " fn set_value(ref self: TContractState, value: u32);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod SimpleContract {\n" -#~ " use starknet::{get_caller_address, ContractAddress};\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " value: u32,\n" -#~ " owner: ContractAddress\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(ref self: ContractState, initial_value: u32) {\n" -#~ " self.value.write(initial_value);\n" -#~ " self.owner.write(get_caller_address());\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl SimpleContract of super::ISimpleContract {\n" -#~ " fn get_value(self: @ContractState) -> u32 {\n" -#~ " self.value.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn get_owner(self: @ContractState) -> ContractAddress {\n" -#~ " self.owner.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn set_value(ref self: ContractState, value: u32) {\n" -#~ " assert(self.owner.read() == get_caller_address(), 'Not " -#~ "owner');\n" -#~ " self.value.write(value);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "use starknet::ContractAddress;\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait ISimpleContract {\n" -#~ " fn get_value(self: @TContractState) -> u32;\n" -#~ " fn get_owner(self: @TContractState) -> ContractAddress;\n" -#~ " fn set_value(ref self: TContractState, value: u32);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod SimpleContract {\n" -#~ " use starknet::{get_caller_address, ContractAddress};\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " value: u32,\n" -#~ " owner: ContractAddress\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(ref self: ContractState, initial_value: u32) {\n" -#~ " self.value.write(initial_value);\n" -#~ " self.owner.write(get_caller_address());\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl SimpleContract of super::ISimpleContract {\n" -#~ " fn get_value(self: @ContractState) -> u32 {\n" -#~ " self.value.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn get_owner(self: @ContractState) -> ContractAddress {\n" -#~ " self.owner.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn set_value(ref self: ContractState, value: u32) {\n" -#~ " assert(self.owner.read() == get_caller_address(), ‘Not " -#~ "owner’);\n" -#~ " self.value.write(value);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "#[cfg(test)]\n" -#~ "mod tests {\n" -#~ " // Import the interface and dispatcher to be able to interact with " -#~ "the contract.\n" -#~ " use sbe_testing::contract::{\n" -#~ " ISimpleContract, SimpleContract, ISimpleContractDispatcher, " -#~ "ISimpleContractDispatcherTrait\n" -#~ " };\n" -#~ "\n" -#~ " // Import the deploy syscall to be able to deploy the contract.\n" -#~ " use starknet::class_hash::Felt252TryIntoClassHash;\n" -#~ " use starknet::{\n" -#~ " deploy_syscall, ContractAddress, get_caller_address, " -#~ "get_contract_address,\n" -#~ " contract_address_const\n" -#~ " };\n" -#~ "\n" -#~ " // Use starknet test utils to fake the transaction context.\n" -#~ " use starknet::testing::{set_caller_address, set_contract_address};\n" -#~ "\n" -#~ " // Deploy the contract and return its dispatcher.\n" -#~ " fn deploy(initial_value: u32) -> ISimpleContractDispatcher {\n" -#~ " // Set up constructor arguments.\n" -#~ " let mut calldata = ArrayTrait::new();\n" -#~ " initial_value.serialize(ref calldata);\n" -#~ "\n" -#~ " // Declare and deploy\n" -#~ " let (contract_address, _) = deploy_syscall(\n" -#~ " SimpleContract::TEST_CLASS_HASH.try_into().unwrap(), 0, " -#~ "calldata.span(), false\n" -#~ " )\n" -#~ " .unwrap();\n" -#~ "\n" -#~ " // Return the dispatcher.\n" -#~ " // The dispatcher allows to interact with the contract based on " -#~ "its interface.\n" -#~ " ISimpleContractDispatcher { contract_address }\n" -#~ " }\n" -#~ "\n" -#~ " #[test]\n" -#~ " #[available_gas(2000000000)]\n" -#~ " fn test_deploy() {\n" -#~ " let initial_value: u32 = 10;\n" -#~ " let contract = deploy(initial_value);\n" -#~ "\n" -#~ " assert(contract.get_value() == initial_value, 'wrong initial " -#~ "value');\n" -#~ " assert(contract.get_owner() == get_contract_address(), 'wrong " -#~ "owner');\n" -#~ " }\n" -#~ "\n" -#~ " #[test]\n" -#~ " #[available_gas(2000000000)]\n" -#~ " fn test_set_as_owner() {\n" -#~ " // Fake the caller address to address 1\n" -#~ " let owner = contract_address_const::<1>();\n" -#~ " set_contract_address(owner);\n" -#~ "\n" -#~ " let contract = deploy(10);\n" -#~ " assert(contract.get_owner() == owner, 'wrong owner');\n" -#~ "\n" -#~ " // Fake the contract address to address 1\n" -#~ " set_contract_address(owner);\n" -#~ " let new_value: u32 = 20;\n" -#~ " contract.set_value(new_value);\n" -#~ "\n" -#~ " assert(contract.get_value() == new_value, 'wrong value');\n" -#~ " }\n" -#~ "\n" -#~ " #[test]\n" -#~ " #[should_panic]\n" -#~ " #[available_gas(2000000000)]\n" -#~ " fn test_set_not_owner() {\n" -#~ " let owner = contract_address_const::<1>();\n" -#~ " set_contract_address(owner);\n" -#~ "\n" -#~ " let contract = deploy(10);\n" -#~ "\n" -#~ " let not_owner = contract_address_const::<2>();\n" -#~ " set_contract_address(not_owner);\n" -#~ "\n" -#~ " let new_value: u32 = 20;\n" -#~ " contract.set_value(new_value);\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[cfg(test)]\n" -#~ "mod tests {\n" -#~ " // 导入接口和调度器,以便能够与合约进行交互\n" -#~ " use sbe_testing::contract::{\n" -#~ " ISimpleContract, SimpleContract, ISimpleContractDispatcher, " -#~ "ISimpleContractDispatcherTrait\n" -#~ " };\n" -#~ "\n" -#~ " //导入部署 deploy_syscall,以便能够部署合约\n" -#~ " use starknet::class_hash::Felt252TryIntoClassHash;\n" -#~ " use starknet::{\n" -#~ " deploy_syscall, ContractAddress, get_caller_address, " -#~ "get_contract_address,\n" -#~ " contract_address_const\n" -#~ " };\n" -#~ "\n" -#~ " // 使用 starknet 测试工具来模拟上下文交易\n" -#~ " use starknet::testing::{set_caller_address, set_contract_address};\n" -#~ "\n" -#~ " // 部署合约并返回其调度器\n" -#~ " fn deploy(initial_value: u32) -> ISimpleContractDispatcher {\n" -#~ " // Set up constructor arguments.\n" -#~ " let mut calldata = ArrayTrait::new();\n" -#~ " initial_value.serialize(ref calldata);\n" -#~ "\n" -#~ " // 声明并部署合约\n" -#~ " let (contract_address, _) = deploy_syscall(\n" -#~ " SimpleContract::TEST_CLASS_HASH.try_into().unwrap(), 0, " -#~ "calldata.span(), false\n" -#~ " )\n" -#~ " .unwrap();\n" -#~ "\n" -#~ " // 返回调度器\n" -#~ " // 调度器允许根据合约的接口与其进行交互\n" -#~ " ISimpleContractDispatcher { contract_address }\n" -#~ " }\n" -#~ "\n" -#~ " #[test]\n" -#~ " #[available_gas(2000000000)]\n" -#~ " fn test_deploy() {\n" -#~ " let initial_value: u32 = 10;\n" -#~ " let contract = deploy(initial_value);\n" -#~ "\n" -#~ " assert(contract.get_value() == initial_value, ‘wrong initial " -#~ "value’);\n" -#~ " assert(contract.get_owner() == get_contract_address(), ‘wrong " -#~ "owner’);\n" -#~ " }\n" -#~ "\n" -#~ " #[test]\n" -#~ " #[available_gas(2000000000)]\n" -#~ " fn test_set_as_owner() {\n" -#~ " // 将调用者地址设置为地址1\n" -#~ " let owner = contract_address_const::<1>();\n" -#~ " set_contract_address(owner);\n" -#~ "\n" -#~ " let contract = deploy(10);\n" -#~ " assert(contract.get_owner() == owner, ‘wrong owner’);\n" -#~ "\n" -#~ " // 将合约地址设置为地址1\n" -#~ " set_contract_address(owner);\n" -#~ " let new_value: u32 = 20;\n" -#~ " contract.set_value(new_value);\n" -#~ "\n" -#~ " assert(contract.get_value() == new_value, ‘wrong value’);\n" -#~ " }\n" -#~ "\n" -#~ " #[test]\n" -#~ " #[should_panic]\n" -#~ " #[available_gas(2000000000)]\n" -#~ " fn test_set_not_owner() {\n" -#~ " let owner = contract_address_const::<1>();\n" -#~ " set_contract_address(owner);\n" -#~ "\n" -#~ " let contract = deploy(10);\n" -#~ "\n" -#~ " let not_owner = contract_address_const::<2>();\n" -#~ " set_contract_address(not_owner);\n" -#~ "\n" -#~ " let new_value: u32 = 20;\n" -#~ " contract.set_value(new_value);\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "- `set_caller_address(address: ContractAddress)`\n" -#~ "- `set_contract_address(address: ContractAddress)`\n" -#~ "- `set_block_number(block_number: u64)`\n" -#~ "- `set_block_timestamp(block_timestamp: u64)`\n" -#~ "- `set_account_contract_address(address: ContractAddress)`\n" -#~ "- `set_max_fee(fee: u128)`" -#~ msgstr "" -#~ "- `set_caller_address(address: ContractAddress)`\n" -#~ "- `set_contract_address(address: ContractAddress)`\n" -#~ "- `set_block_number(block_number: u64)`\n" -#~ "- `set_block_timestamp(block_timestamp: u64)`\n" -#~ "- `set_account_contract_address(address: ContractAddress)`\n" -#~ "- `set_max_fee(fee: u128)`" - -#~ msgid "" -#~ "- `get_caller_address() -> ContractAddress`\n" -#~ "- `get_contract_address() -> ContractAddress`\n" -#~ "- `get_block_info() -> Box`\n" -#~ "- `get_tx_info() -> Box`\n" -#~ "- `get_block_timestamp() -> u64`\n" -#~ "- `get_block_number() -> u64`" -#~ msgstr "" -#~ "- `get_caller_address() -> ContractAddress`\n" -#~ "- `get_contract_address() -> ContractAddress`\n" -#~ "- `get_block_info() -> Box`\n" -#~ "- `get_tx_info() -> Box`\n" -#~ "- `get_block_timestamp() -> u64`\n" -#~ "- `get_block_number() -> u64`" - -#~ msgid "" -#~ msgstr "" - -#~ msgid "
Last change: 2023-10-31
" -#~ msgstr "
Last change: 2023-10-31
" - -#~ msgid "" -#~ "```rust\n" -#~ " let felt: felt252 = 100;\n" -#~ " let felt_as_str = 'Hello Starknet!';\n" -#~ "\n" -#~ " let felt = felt + felt_as_str;\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ " let felt: felt252 = 100;\n" -#~ " let felt_as_str = ‘Hello Starknet!’;\n" -#~ "\n" -#~ " let felt = felt + felt_as_str;\n" -#~ "```" - -#~ msgid "# Mapping" -#~ msgstr "# Mapping" - -#~ msgid "" -#~ "```rust\n" -#~ "use starknet::ContractAddress;\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IMappingExample {\n" -#~ " fn register_user(ref self: TContractState, student_add: " -#~ "ContractAddress, studentName: felt252);\n" -#~ " fn record_student_score(\n" -#~ " ref self: TContractState, student_add: ContractAddress, subject: " -#~ "felt252, score: u16\n" -#~ " );\n" -#~ " fn view_student_name(self: @TContractState, student_add: " -#~ "ContractAddress) -> felt252;\n" -#~ " fn view_student_score(\n" -#~ " self: @TContractState, student_add: ContractAddress, subject: " -#~ "felt252\n" -#~ " ) -> u16;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod MappingContract {\n" -#~ " use starknet::ContractAddress;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " students_name: LegacyMap::,\n" -#~ " students_result_record: LegacyMap::<(ContractAddress, felt252), " -#~ "u16>,\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl External of super::IMappingExample {\n" -#~ " fn register_user(\n" -#~ " ref self: ContractState, student_add: ContractAddress, " -#~ "studentName: felt252\n" -#~ " ) {\n" -#~ " self.students_name.write(student_add, studentName);\n" -#~ " }\n" -#~ "\n" -#~ " fn record_student_score(\n" -#~ " ref self: ContractState, student_add: ContractAddress, " -#~ "subject: felt252, score: u16\n" -#~ " ) {\n" -#~ " self.students_result_record.write((student_add, subject), " -#~ "score);\n" -#~ " }\n" -#~ "\n" -#~ " fn view_student_name(self: @ContractState, student_add: " -#~ "ContractAddress) -> felt252 {\n" -#~ " self.students_name.read(student_add)\n" -#~ " }\n" -#~ "\n" -#~ " fn view_student_score(\n" -#~ " self: @ContractState, student_add: ContractAddress, subject: " -#~ "felt252\n" -#~ " ) -> u16 {\n" -#~ " // for a 2D mapping its important to take note of the amount " -#~ "of brackets being used.\n" -#~ " self.students_result_record.read((student_add, subject))\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "use starknet::ContractAddress;\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IMappingExample {\n" -#~ " fn register_user(ref self: TContractState, student_add: " -#~ "ContractAddress, studentName: felt252);\n" -#~ " fn record_student_score(\n" -#~ " ref self: TContractState, student_add: ContractAddress, subject: " -#~ "felt252, score: u16\n" -#~ " );\n" -#~ " fn view_student_name(self: @TContractState, student_add: " -#~ "ContractAddress) -> felt252;\n" -#~ " fn view_student_score(\n" -#~ " self: @TContractState, student_add: ContractAddress, subject: " -#~ "felt252\n" -#~ " ) -> u16;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod MappingContract {\n" -#~ " use starknet::ContractAddress;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " students_name: LegacyMap::,\n" -#~ " students_result_record: LegacyMap::<(ContractAddress, felt252), " -#~ "u16>,\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl External of super::IMappingExample {\n" -#~ " fn register_user(\n" -#~ " ref self: ContractState, student_add: ContractAddress, " -#~ "studentName: felt252\n" -#~ " ) {\n" -#~ " self.students_name.write(student_add, studentName);\n" -#~ " }\n" -#~ "\n" -#~ " fn record_student_score(\n" -#~ " ref self: ContractState, student_add: ContractAddress, " -#~ "subject: felt252, score: u16\n" -#~ " ) {\n" -#~ " self.students_result_record.write((student_add, subject), " -#~ "score);\n" -#~ " }\n" -#~ "\n" -#~ " fn view_student_name(self: @ContractState, student_add: " -#~ "ContractAddress) -> felt252 {\n" -#~ " self.students_name.read(student_add)\n" -#~ " }\n" -#~ "\n" -#~ " fn view_student_score(\n" -#~ " self: @ContractState, student_add: ContractAddress, subject: " -#~ "felt252\n" -#~ " ) -> u16 {\n" -#~ " // 对于二维映射,重要的是注意使用的括号数量。\n" -#~ " self.students_result_record.read((student_add, subject))\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Arrays" -#~ msgstr "# 数组" - -#~ msgid "" -#~ "```rust\n" -#~ "trait ArrayTrait {\n" -#~ " fn new() -> Array;\n" -#~ " fn append(ref self: Array, value: T);\n" -#~ " fn pop_front(ref self: Array) -> Option nopanic;\n" -#~ " fn pop_front_consume(self: Array) -> Option<(Array, T)> " -#~ "nopanic;\n" -#~ " fn get(self: @Array, index: usize) -> Option>;\n" -#~ " fn at(self: @Array, index: usize) -> @T;\n" -#~ " fn len(self: @Array) -> usize;\n" -#~ " fn is_empty(self: @Array) -> bool;\n" -#~ " fn span(self: @Array) -> Span;\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "trait ArrayTrait {\n" -#~ " fn new() -> Array;\n" -#~ " fn append(ref self: Array, value: T);\n" -#~ " fn pop_front(ref self: Array) -> Option nopanic;\n" -#~ " fn pop_front_consume(self: Array) -> Option<(Array, T)> " -#~ "nopanic;\n" -#~ " fn get(self: @Array, index: usize) -> Option>;\n" -#~ " fn at(self: @Array, index: usize) -> @T;\n" -#~ " fn len(self: @Array) -> usize;\n" -#~ " fn is_empty(self: @Array) -> bool;\n" -#~ " fn span(self: @Array) -> Span;\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "fn array() -> bool {\n" -#~ " let mut arr = ArrayTrait::::new();\n" -#~ " arr.append(10);\n" -#~ " arr.append(20);\n" -#~ " arr.append(30);\n" -#~ "\n" -#~ " assert(arr.len() == 3, 'array length should be 3');\n" -#~ "\n" -#~ " let first_value = arr.pop_front().unwrap();\n" -#~ " assert(first_value == 10, 'first value should match');\n" -#~ "\n" -#~ " let second_value = *arr.at(0);\n" -#~ " assert(second_value == 20, 'second value should match');\n" -#~ "\n" -#~ " // Returns true if an array is empty, then false if it isn't.\n" -#~ " arr.is_empty()\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "fn array() -> bool {\n" -#~ " let mut arr = ArrayTrait::::new();\n" -#~ " arr.append(10);\n" -#~ " arr.append(20);\n" -#~ " arr.append(30);\n" -#~ "\n" -#~ " assert(arr.len() == 3, ‘array length should be 3’);\n" -#~ "\n" -#~ " let first_value = arr.pop_front().unwrap();\n" -#~ " assert(first_value == 10, ‘first value should match’);\n" -#~ "\n" -#~ " let second_value = *arr.at(0);\n" -#~ " assert(second_value == 20, ‘second value should match’);\n" -#~ "\n" -#~ " // 如果数组为空,返回 true;如果不为空,返回 false。\n" -#~ " arr.is_empty()\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Loop" -#~ msgstr "# 循环" - -#~ msgid "" -#~ "```rust\n" -#~ " let mut arr = ArrayTrait::new();\n" -#~ "\n" -#~ " // Same as ~ while (i < 10) arr.append(i++);\n" -#~ " let mut i: u32 = 0;\n" -#~ " let limit = 10;\n" -#~ " loop {\n" -#~ " if i == limit {\n" -#~ " break;\n" -#~ " };\n" -#~ "\n" -#~ " arr.append(i);\n" -#~ "\n" -#~ " i += 1;\n" -#~ " };\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ " let mut arr = ArrayTrait::new();\n" -#~ "\n" -#~ " // 与 ~ while (i < 10) arr.append(i++); 相同\n" -#~ " let mut i: u32 = 0;\n" -#~ " let limit = 10;\n" -#~ " loop {\n" -#~ " if i == limit {\n" -#~ " break;\n" -#~ " };\n" -#~ "\n" -#~ " arr.append(i);\n" -#~ "\n" -#~ " i += 1;\n" -#~ " };\n" -#~ "```" - -#~ msgid "# Match" -#~ msgstr "# 分支" - -#~ msgid "" -#~ "```rust\n" -#~ "#[derive(Drop, Serde)]\n" -#~ "enum Colour {\n" -#~ " Red,\n" -#~ " Blue,\n" -#~ " Green,\n" -#~ " Orange,\n" -#~ " Black\n" -#~ "}\n" -#~ "\n" -#~ "#[derive(Drop, Serde)]\n" -#~ "enum Coin {\n" -#~ " Penny,\n" -#~ " Nickel,\n" -#~ " Dime,\n" -#~ " Quarter,\n" -#~ "}\n" -#~ "\n" -#~ "fn value_in_cents(coin: Coin) -> felt252 {\n" -#~ " match coin {\n" -#~ " Coin::Penny => 1,\n" -#~ " Coin::Nickel => 5,\n" -#~ " Coin::Dime => 10,\n" -#~ " Coin::Quarter => 25,\n" -#~ " }\n" -#~ "}\n" -#~ "\n" -#~ "fn specified_colour(colour: Colour) -> felt252 {\n" -#~ " let mut response: felt252 = '';\n" -#~ "\n" -#~ " match colour {\n" -#~ " Colour::Red => { response = 'You passed in Red'; },\n" -#~ " Colour::Blue => { response = 'You passed in Blue'; },\n" -#~ " Colour::Green => { response = 'You passed in Green'; },\n" -#~ " Colour::Orange => { response = 'You passed in Orange'; },\n" -#~ " Colour::Black => { response = 'You passed in Black'; },\n" -#~ " };\n" -#~ "\n" -#~ " response\n" -#~ "}\n" -#~ "\n" -#~ "fn quiz(num: felt252) -> felt252 {\n" -#~ " let mut response: felt252 = '';\n" -#~ "\n" -#~ " match num {\n" -#~ " 0 => { response = 'You failed' },\n" -#~ " _ => { response = 'You Passed' },\n" -#~ " };\n" -#~ "\n" -#~ " response\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[derive(Drop, Serde)]\n" -#~ "enum Colour {\n" -#~ " Red,\n" -#~ " Blue,\n" -#~ " Green,\n" -#~ " Orange,\n" -#~ " Black\n" -#~ "}\n" -#~ "\n" -#~ "#[derive(Drop, Serde)]\n" -#~ "enum Coin {\n" -#~ " Penny,\n" -#~ " Nickel,\n" -#~ " Dime,\n" -#~ " Quarter,\n" -#~ "}\n" -#~ "\n" -#~ "fn value_in_cents(coin: Coin) -> felt252 {\n" -#~ " match coin {\n" -#~ " Coin::Penny => 1,\n" -#~ " Coin::Nickel => 5,\n" -#~ " Coin::Dime => 10,\n" -#~ " Coin::Quarter => 25,\n" -#~ " }\n" -#~ "}\n" -#~ "\n" -#~ "fn specified_colour(colour: Colour) -> felt252 {\n" -#~ " let mut response: felt252 = ‘’;\n" -#~ "\n" -#~ " match colour {\n" -#~ " Colour::Red => { response = ‘You passed in Red’; },\n" -#~ " Colour::Blue => { response = ‘You passed in Blue’; },\n" -#~ " Colour::Green => { response = ‘You passed in Green’; },\n" -#~ " Colour::Orange => { response = ‘You passed in Orange’; },\n" -#~ " Colour::Black => { response = ‘You passed in Black’; },\n" -#~ " };\n" -#~ "\n" -#~ " response\n" -#~ "}\n" -#~ "\n" -#~ "fn quiz(num: felt252) -> felt252 {\n" -#~ " let mut response: felt252 = ‘’;\n" -#~ "\n" -#~ " match num {\n" -#~ " 0 => { response = ‘You failed’ },\n" -#~ " _ => { response = ‘You Passed’ },\n" -#~ " };\n" -#~ "\n" -#~ " response\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Tuples" -#~ msgstr "# 元组" - -#~ msgid "" -#~ "```rust\n" -#~ " let address = \"0x000\";\n" -#~ " let age = 20;\n" -#~ " let active = true;\n" -#~ "\n" -#~ " // Create tuple\n" -#~ " let user_tuple = (address, age, active);\n" -#~ "\n" -#~ " // Access tuple\n" -#~ " let (address, age, active) = stored_tuple;\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ " let address = “0x000”;\n" -#~ " let age = 20;\n" -#~ " let active = true;\n" -#~ "\n" -#~ " // Create tuple\n" -#~ " let user_tuple = (address, age, active);\n" -#~ "\n" -#~ " // Access tuple\n" -#~ " let (address, age, active) = stored_tuple;\n" -#~ "```" - -#~ msgid "# Struct" -#~ msgstr "# 结构体" - -#~ msgid "" -#~ "```rust\n" -#~ "// With Store, you can store Data's structs in the storage part of " -#~ "contracts.\n" -#~ "#[derive(Drop, starknet::Store)]\n" -#~ "struct Data {\n" -#~ " address: starknet::ContractAddress,\n" -#~ " age: u8\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "// 使用 Store,您可以将 Data 结构体存储在合约的存储部分。\n" -#~ "#[derive(Drop, starknet::Store)]\n" -#~ "struct Data {\n" -#~ " address: starknet::ContractAddress,\n" -#~ " age: u8\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Type casting" -#~ msgstr "# 类型转换" - -#~ msgid "" -#~ "```rust\n" -#~ " let a_number: u32 = 15;\n" -#~ " let my_felt252 = 15;\n" -#~ "\n" -#~ " // Since a u32 might not fit in a u8 and a u16, we need to use " -#~ "try_into,\n" -#~ " // then unwrap the Option type thats returned.\n" -#~ " let new_u8: u8 = a_number.try_into().unwrap();\n" -#~ " let new_u16: u16 = a_number.try_into().unwrap();\n" -#~ "\n" -#~ " // since new_u32 is the of the same type (u32) as rand_number, we can " -#~ "directly assign them,\n" -#~ " // or use the .into() method.\n" -#~ " let new_u32: u32 = a_number;\n" -#~ "\n" -#~ " // When typecasting from a smaller size to an equal or larger size we " -#~ "use the .into() method.\n" -#~ " // Note: u64 and u128 are larger than u32, so a u32 type will always " -#~ "fit into them.\n" -#~ " let new_u64: u64 = a_number.into();\n" -#~ " let new_u128: u128 = a_number.into();\n" -#~ "\n" -#~ " // Since a felt252 is smaller than a u256, we can use the into() " -#~ "method\n" -#~ " let new_u256: u256 = my_felt252.into();\n" -#~ " let new_felt252: felt252 = new_u16.into();\n" -#~ "\n" -#~ " //note a usize is smaller than a felt so we use the try_into\n" -#~ " let new_usize: usize = my_felt252.try_into().unwrap();\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ " let a_number: u32 = 15;\n" -#~ " let my_felt252 = 15;\n" -#~ "\n" -#~ " // 由于 u32 可能不匹配 u8 和 u16,我们需要使用 try_into\n" -#~ " // 然后解包返回的 Option 类型。\n" -#~ " let new_u8: u8 = a_number.try_into().unwrap();\n" -#~ " let new_u16: u16 = a_number.try_into().unwrap();\n" -#~ "\n" -#~ " // 由于 new_u32 的类型(u32)与 a_number 相同,我们可以直接赋值\n" -#~ " // 或使用 .into() 方法\n" -#~ " let new_u32: u32 = a_number;\n" -#~ "\n" -#~ " // 当从较小的大小类型强制转换为相等或较大的大小类型时,我们使用 ." -#~ "into() 方法\n" -#~ " // 注意:u64 和 u128 大于 u32,所以 u32 类型将始终适合其中\n" -#~ " let new_u64: u64 = a_number.into();\n" -#~ " let new_u128: u128 = a_number.into();\n" -#~ "\n" -#~ " // 由于 felt252 比 u256 小,我们可以使用 into() 方法\n" -#~ " let new_u256: u256 = my_felt252.into();\n" -#~ " let new_felt252: felt252 = new_u16.into();\n" -#~ "\n" -#~ " //注意,usize 比 felt252 小,因此我们使用 try_into\n" -#~ " let new_usize: usize = my_felt252.try_into().unwrap();\n" -#~ "```" - -#~ msgid "# Upgradeable Contract" -#~ msgstr "# 可升级合约" - -#~ msgid "" -#~ "```rust\n" -#~ "use starknet::class_hash::ClassHash;\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IUpgradeableContract {\n" -#~ " fn upgrade(ref self: TContractState, impl_hash: ClassHash);\n" -#~ " fn version(self: @TContractState) -> u8;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod UpgradeableContract_V0 {\n" -#~ " use starknet::class_hash::ClassHash;\n" -#~ " use starknet::SyscallResultTrait;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ "\n" -#~ " #[event]\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " enum Event {\n" -#~ " Upgraded: Upgraded\n" -#~ " }\n" -#~ "\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " struct Upgraded {\n" -#~ " implementation: ClassHash\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl UpgradeableContract of super::" -#~ "IUpgradeableContract {\n" -#~ " fn upgrade(ref self: ContractState, impl_hash: ClassHash) {\n" -#~ " assert(!impl_hash.is_zero(), 'Class hash cannot be zero');\n" -#~ " starknet::replace_class_syscall(impl_hash).unwrap_syscall();\n" -#~ " self.emit(Event::Upgraded(Upgraded { implementation: " -#~ "impl_hash }))\n" -#~ " }\n" -#~ "\n" -#~ " fn version(self: @ContractState) -> u8 {\n" -#~ " 0\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "use starknet::class_hash::ClassHash;\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IUpgradeableContract {\n" -#~ " fn upgrade(ref self: TContractState, impl_hash: ClassHash);\n" -#~ " fn version(self: @TContractState) -> u8;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod UpgradeableContract_V0 {\n" -#~ " use starknet::class_hash::ClassHash;\n" -#~ " use starknet::SyscallResultTrait;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ "\n" -#~ " #[event]\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " enum Event {\n" -#~ " Upgraded: Upgraded\n" -#~ " }\n" -#~ "\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " struct Upgraded {\n" -#~ " implementation: ClassHash\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl UpgradeableContract of super::" -#~ "IUpgradeableContract {\n" -#~ " fn upgrade(ref self: ContractState, impl_hash: ClassHash) {\n" -#~ " assert(!impl_hash.is_zero(), 'Class hash cannot be zero');\n" -#~ " starknet::replace_class_syscall(impl_hash).unwrap_syscall();\n" -#~ " self.emit(Event::Upgraded(Upgraded { implementation: " -#~ "impl_hash }))\n" -#~ " }\n" -#~ "\n" -#~ " fn version(self: @ContractState) -> u8 {\n" -#~ " 0\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "use starknet::class_hash::ClassHash;\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IUpgradeableContract {\n" -#~ " fn upgrade(ref self: TContractState, impl_hash: ClassHash);\n" -#~ " fn version(self: @TContractState) -> u8;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod UpgradeableContract_V1 {\n" -#~ " use starknet::class_hash::ClassHash;\n" -#~ " use starknet::SyscallResultTrait;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ "\n" -#~ " #[event]\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " enum Event {\n" -#~ " Upgraded: Upgraded\n" -#~ " }\n" -#~ "\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " struct Upgraded {\n" -#~ " implementation: ClassHash\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl UpgradeableContract of super::" -#~ "IUpgradeableContract {\n" -#~ " fn upgrade(ref self: ContractState, impl_hash: ClassHash) {\n" -#~ " assert(!impl_hash.is_zero(), 'Class hash cannot be zero');\n" -#~ " starknet::replace_class_syscall(impl_hash).unwrap_syscall();\n" -#~ " self.emit(Event::Upgraded(Upgraded { implementation: " -#~ "impl_hash }))\n" -#~ " }\n" -#~ "\n" -#~ " fn version(self: @ContractState) -> u8 {\n" -#~ " 1\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "use starknet::class_hash::ClassHash;\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IUpgradeableContract {\n" -#~ " fn upgrade(ref self: TContractState, impl_hash: ClassHash);\n" -#~ " fn version(self: @TContractState) -> u8;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod UpgradeableContract_V1 {\n" -#~ " use starknet::class_hash::ClassHash;\n" -#~ " use starknet::SyscallResultTrait;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ "\n" -#~ " #[event]\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " enum Event {\n" -#~ " Upgraded: Upgraded\n" -#~ " }\n" -#~ "\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " struct Upgraded {\n" -#~ " implementation: ClassHash\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl UpgradeableContract of super::" -#~ "IUpgradeableContract {\n" -#~ " fn upgrade(ref self: ContractState, impl_hash: ClassHash) {\n" -#~ " assert(!impl_hash.is_zero(), 'Class hash cannot be zero');\n" -#~ " starknet::replace_class_syscall(impl_hash).unwrap_syscall();\n" -#~ " self.emit(Event::Upgraded(Upgraded { implementation: " -#~ "impl_hash }))\n" -#~ " }\n" -#~ "\n" -#~ " fn version(self: @ContractState) -> u8 {\n" -#~ " 1\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "use starknet::{ContractAddress};\n" -#~ "\n" -#~ "// In order to make contract calls within our Vault,\n" -#~ "// we need to have the interface of the remote ERC20 contract defined to " -#~ "import the Dispatcher.\n" -#~ "#[starknet::interface]\n" -#~ "trait IERC20 {\n" -#~ " fn name(self: @TContractState) -> felt252;\n" -#~ " fn symbol(self: @TContractState) -> felt252;\n" -#~ " fn decimals(self: @TContractState) -> u8;\n" -#~ " fn total_supply(self: @TContractState) -> u256;\n" -#~ " fn balance_of(self: @TContractState, account: ContractAddress) -> " -#~ "u256;\n" -#~ " fn allowance(self: @TContractState, owner: ContractAddress, spender: " -#~ "ContractAddress) -> u256;\n" -#~ " fn transfer(ref self: TContractState, recipient: ContractAddress, " -#~ "amount: u256) -> bool;\n" -#~ " fn transfer_from(\n" -#~ " ref self: TContractState, sender: ContractAddress, recipient: " -#~ "ContractAddress, amount: u256\n" -#~ " ) -> bool;\n" -#~ " fn approve(ref self: TContractState, spender: ContractAddress, " -#~ "amount: u256) -> bool;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait ISimpleVault {\n" -#~ " fn deposit(ref self: TContractState, amount: u256);\n" -#~ " fn withdraw(ref self: TContractState, shares: u256);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod SimpleVault {\n" -#~ " use super::{IERC20Dispatcher, IERC20DispatcherTrait};\n" -#~ " use starknet::{ContractAddress, get_caller_address, " -#~ "get_contract_address};\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " token: IERC20Dispatcher,\n" -#~ " total_supply: u256,\n" -#~ " balance_of: LegacyMap\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(ref self: ContractState, token: ContractAddress) {\n" -#~ " self.token.write(IERC20Dispatcher { contract_address: token });\n" -#~ " }\n" -#~ "\n" -#~ " #[generate_trait]\n" -#~ " impl PrivateFunctions of PrivateFunctionsTrait {\n" -#~ " fn _mint(ref self: ContractState, to: ContractAddress, shares: " -#~ "u256) {\n" -#~ " self.total_supply.write(self.total_supply.read() + shares);\n" -#~ " self.balance_of.write(to, self.balance_of.read(to) + " -#~ "shares);\n" -#~ " }\n" -#~ "\n" -#~ " fn _burn(ref self: ContractState, from: ContractAddress, shares: " -#~ "u256) {\n" -#~ " self.total_supply.write(self.total_supply.read() - shares);\n" -#~ " self.balance_of.write(from, self.balance_of.read(from) - " -#~ "shares);\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl SimpleVault of super::ISimpleVault {\n" -#~ " fn deposit(ref self: ContractState, amount: u256) {\n" -#~ " // a = amount\n" -#~ " // B = balance of token before deposit\n" -#~ " // T = total supply\n" -#~ " // s = shares to mint\n" -#~ " //\n" -#~ " // (T + s) / T = (a + B) / B \n" -#~ " //\n" -#~ " // s = aT / B\n" -#~ " let caller = get_caller_address();\n" -#~ " let this = get_contract_address();\n" -#~ "\n" -#~ " let mut shares = 0;\n" -#~ " if self.total_supply.read() == 0 {\n" -#~ " shares = amount;\n" -#~ " } else {\n" -#~ " let balance = self.token.read().balance_of(this);\n" -#~ " shares = (amount * self.total_supply.read()) / balance;\n" -#~ " }\n" -#~ "\n" -#~ " PrivateFunctions::_mint(ref self, caller, shares);\n" -#~ " self.token.read().transfer_from(caller, this, amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn withdraw(ref self: ContractState, shares: u256) {\n" -#~ " // a = amount\n" -#~ " // B = balance of token before withdraw\n" -#~ " // T = total supply\n" -#~ " // s = shares to burn\n" -#~ " //\n" -#~ " // (T - s) / T = (B - a) / B \n" -#~ " //\n" -#~ " // a = sB / T\n" -#~ " let caller = get_caller_address();\n" -#~ " let this = get_contract_address();\n" -#~ "\n" -#~ " let balance = self.token.read().balance_of(this);\n" -#~ " let amount = (shares * balance) / self.total_supply.read();\n" -#~ " PrivateFunctions::_burn(ref self, caller, shares);\n" -#~ " self.token.read().transfer(caller, amount);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "use starknet::{ContractAddress};\n" -#~ "\n" -#~ "// In order to make contract calls within our Vault,\n" -#~ "// we need to have the interface of the remote ERC20 contract defined to " -#~ "import the Dispatcher.\n" -#~ "#[starknet::interface]\n" -#~ "trait IERC20 {\n" -#~ " fn name(self: @TContractState) -> felt252;\n" -#~ " fn symbol(self: @TContractState) -> felt252;\n" -#~ " fn decimals(self: @TContractState) -> u8;\n" -#~ " fn total_supply(self: @TContractState) -> u256;\n" -#~ " fn balance_of(self: @TContractState, account: ContractAddress) -> " -#~ "u256;\n" -#~ " fn allowance(self: @TContractState, owner: ContractAddress, spender: " -#~ "ContractAddress) -> u256;\n" -#~ " fn transfer(ref self: TContractState, recipient: ContractAddress, " -#~ "amount: u256) -> bool;\n" -#~ " fn transfer_from(\n" -#~ " ref self: TContractState, sender: ContractAddress, recipient: " -#~ "ContractAddress, amount: u256\n" -#~ " ) -> bool;\n" -#~ " fn approve(ref self: TContractState, spender: ContractAddress, " -#~ "amount: u256) -> bool;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait ISimpleVault {\n" -#~ " fn deposit(ref self: TContractState, amount: u256);\n" -#~ " fn withdraw(ref self: TContractState, shares: u256);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod SimpleVault {\n" -#~ " use super::{IERC20Dispatcher, IERC20DispatcherTrait};\n" -#~ " use starknet::{ContractAddress, get_caller_address, " -#~ "get_contract_address};\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " token: IERC20Dispatcher,\n" -#~ " total_supply: u256,\n" -#~ " balance_of: LegacyMap\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(ref self: ContractState, token: ContractAddress) {\n" -#~ " self.token.write(IERC20Dispatcher { contract_address: token });\n" -#~ " }\n" -#~ "\n" -#~ " #[generate_trait]\n" -#~ " impl PrivateFunctions of PrivateFunctionsTrait {\n" -#~ " fn _mint(ref self: ContractState, to: ContractAddress, shares: " -#~ "u256) {\n" -#~ " self.total_supply.write(self.total_supply.read() + shares);\n" -#~ " self.balance_of.write(to, self.balance_of.read(to) + " -#~ "shares);\n" -#~ " }\n" -#~ "\n" -#~ " fn _burn(ref self: ContractState, from: ContractAddress, shares: " -#~ "u256) {\n" -#~ " self.total_supply.write(self.total_supply.read() - shares);\n" -#~ " self.balance_of.write(from, self.balance_of.read(from) - " -#~ "shares);\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl SimpleVault of super::ISimpleVault {\n" -#~ " fn deposit(ref self: ContractState, amount: u256) {\n" -#~ " // a = amount\n" -#~ " // B = balance of token before deposit\n" -#~ " // T = total supply\n" -#~ " // s = shares to mint\n" -#~ " //\n" -#~ " // (T + s) / T = (a + B) / B \n" -#~ " //\n" -#~ " // s = aT / B\n" -#~ " let caller = get_caller_address();\n" -#~ " let this = get_contract_address();\n" -#~ "\n" -#~ " let mut shares = 0;\n" -#~ " if self.total_supply.read() == 0 {\n" -#~ " shares = amount;\n" -#~ " } else {\n" -#~ " let balance = self.token.read().balance_of(this);\n" -#~ " shares = (amount * self.total_supply.read()) / balance;\n" -#~ " }\n" -#~ "\n" -#~ " PrivateFunctions::_mint(ref self, caller, shares);\n" -#~ " self.token.read().transfer_from(caller, this, amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn withdraw(ref self: ContractState, shares: u256) {\n" -#~ " // a = amount\n" -#~ " // B = balance of token before withdraw\n" -#~ " // T = total supply\n" -#~ " // s = shares to burn\n" -#~ " //\n" -#~ " // (T - s) / T = (B - a) / B \n" -#~ " //\n" -#~ " // a = sB / T\n" -#~ " let caller = get_caller_address();\n" -#~ " let this = get_contract_address();\n" -#~ "\n" -#~ " let balance = self.token.read().balance_of(this);\n" -#~ " let amount = (shares * balance) / self.total_supply.read();\n" -#~ " PrivateFunctions::_burn(ref self, caller, shares);\n" -#~ " self.token.read().transfer(caller, amount);\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "\n" -#~ "```" - -#~ msgid "# ERC20 Token" -#~ msgstr "# ERC20 代币" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IERC20 {\n" -#~ " fn get_name(self: @TContractState) -> felt252;\n" -#~ " fn get_symbol(self: @TContractState) -> felt252;\n" -#~ " fn get_decimals(self: @TContractState) -> u8;\n" -#~ " fn get_total_supply(self: @TContractState) -> felt252;\n" -#~ " fn balance_of(self: @TContractState, account: ContractAddress) -> " -#~ "felt252;\n" -#~ " fn allowance(\n" -#~ " self: @TContractState, owner: ContractAddress, spender: " -#~ "ContractAddress\n" -#~ " ) -> felt252;\n" -#~ " fn transfer(ref self: TContractState, recipient: ContractAddress, " -#~ "amount: felt252);\n" -#~ " fn transfer_from(\n" -#~ " ref self: TContractState,\n" -#~ " sender: ContractAddress,\n" -#~ " recipient: ContractAddress,\n" -#~ " amount: felt252\n" -#~ " );\n" -#~ " fn approve(ref self: TContractState, spender: ContractAddress, " -#~ "amount: felt252);\n" -#~ " fn increase_allowance(ref self: TContractState, spender: " -#~ "ContractAddress, added_value: felt252);\n" -#~ " fn decrease_allowance(\n" -#~ " ref self: TContractState, spender: ContractAddress, " -#~ "subtracted_value: felt252\n" -#~ " );\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IERC20 {\n" -#~ " fn get_name(self: @TContractState) -> felt252;\n" -#~ " fn get_symbol(self: @TContractState) -> felt252;\n" -#~ " fn get_decimals(self: @TContractState) -> u8;\n" -#~ " fn get_total_supply(self: @TContractState) -> felt252;\n" -#~ " fn balance_of(self: @TContractState, account: ContractAddress) -> " -#~ "felt252;\n" -#~ " fn allowance(\n" -#~ " self: @TContractState, owner: ContractAddress, spender: " -#~ "ContractAddress\n" -#~ " ) -> felt252;\n" -#~ " fn transfer(ref self: TContractState, recipient: ContractAddress, " -#~ "amount: felt252);\n" -#~ " fn transfer_from(\n" -#~ " ref self: TContractState,\n" -#~ " sender: ContractAddress,\n" -#~ " recipient: ContractAddress,\n" -#~ " amount: felt252\n" -#~ " );\n" -#~ " fn approve(ref self: TContractState, spender: ContractAddress, " -#~ "amount: felt252);\n" -#~ " fn increase_allowance(ref self: TContractState, spender: " -#~ "ContractAddress, added_value: felt252);\n" -#~ " fn decrease_allowance(\n" -#~ " ref self: TContractState, spender: ContractAddress, " -#~ "subtracted_value: felt252\n" -#~ " );\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::contract]\n" -#~ "mod erc20 {\n" -#~ " use zeroable::Zeroable;\n" -#~ " use starknet::get_caller_address;\n" -#~ " use starknet::contract_address_const;\n" -#~ " use starknet::ContractAddress;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " name: felt252,\n" -#~ " symbol: felt252,\n" -#~ " decimals: u8,\n" -#~ " total_supply: felt252,\n" -#~ " balances: LegacyMap::,\n" -#~ " allowances: LegacyMap::<(ContractAddress, ContractAddress), " -#~ "felt252>,\n" -#~ " }\n" -#~ "\n" -#~ " #[event]\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " enum Event {\n" -#~ " Transfer: Transfer,\n" -#~ " Approval: Approval,\n" -#~ " }\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " struct Transfer {\n" -#~ " from: ContractAddress,\n" -#~ " to: ContractAddress,\n" -#~ " value: felt252,\n" -#~ " }\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " struct Approval {\n" -#~ " owner: ContractAddress,\n" -#~ " spender: ContractAddress,\n" -#~ " value: felt252,\n" -#~ " }\n" -#~ "\n" -#~ " mod Errors {\n" -#~ " const APPROVE_FROM_ZERO: felt252 = 'ERC20: approve from 0';\n" -#~ " const APPROVE_TO_ZERO: felt252 = 'ERC20: approve to 0';\n" -#~ " const TRANSFER_FROM_ZERO: felt252 = 'ERC20: transfer from 0';\n" -#~ " const TRANSFER_TO_ZERO: felt252 = 'ERC20: transfer to 0';\n" -#~ " const BURN_FROM_ZERO: felt252 = 'ERC20: burn from 0';\n" -#~ " const MINT_TO_ZERO: felt252 = 'ERC20: mint to 0';\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(\n" -#~ " ref self: ContractState,\n" -#~ " recipient: ContractAddress,\n" -#~ " name: felt252,\n" -#~ " decimals: u8,\n" -#~ " initial_supply: felt252,\n" -#~ " symbol: felt252\n" -#~ " ) {\n" -#~ " self.name.write(name);\n" -#~ " self.symbol.write(symbol);\n" -#~ " self.decimals.write(decimals);\n" -#~ " self.mint(recipient, initial_supply);\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl IERC20Impl of super::IERC20 {\n" -#~ " fn get_name(self: @ContractState) -> felt252 {\n" -#~ " self.name.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn get_symbol(self: @ContractState) -> felt252 {\n" -#~ " self.symbol.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn get_decimals(self: @ContractState) -> u8 {\n" -#~ " self.decimals.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn get_total_supply(self: @ContractState) -> felt252 {\n" -#~ " self.total_supply.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn balance_of(self: @ContractState, account: ContractAddress) -> " -#~ "felt252 {\n" -#~ " self.balances.read(account)\n" -#~ " }\n" -#~ "\n" -#~ " fn allowance(\n" -#~ " self: @ContractState, owner: ContractAddress, spender: " -#~ "ContractAddress\n" -#~ " ) -> felt252 {\n" -#~ " self.allowances.read((owner, spender))\n" -#~ " }\n" -#~ "\n" -#~ " fn transfer(ref self: ContractState, recipient: ContractAddress, " -#~ "amount: felt252) {\n" -#~ " let sender = get_caller_address();\n" -#~ " self._transfer(sender, recipient, amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn transfer_from(\n" -#~ " ref self: ContractState,\n" -#~ " sender: ContractAddress,\n" -#~ " recipient: ContractAddress,\n" -#~ " amount: felt252\n" -#~ " ) {\n" -#~ " let caller = get_caller_address();\n" -#~ " self.spend_allowance(sender, caller, amount);\n" -#~ " self._transfer(sender, recipient, amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn approve(ref self: ContractState, spender: ContractAddress, " -#~ "amount: felt252) {\n" -#~ " let caller = get_caller_address();\n" -#~ " self.approve_helper(caller, spender, amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn increase_allowance(\n" -#~ " ref self: ContractState, spender: ContractAddress, " -#~ "added_value: felt252\n" -#~ " ) {\n" -#~ " let caller = get_caller_address();\n" -#~ " self\n" -#~ " .approve_helper(\n" -#~ " caller, spender, self.allowances.read((caller, " -#~ "spender)) + added_value\n" -#~ " );\n" -#~ " }\n" -#~ "\n" -#~ " fn decrease_allowance(\n" -#~ " ref self: ContractState, spender: ContractAddress, " -#~ "subtracted_value: felt252\n" -#~ " ) {\n" -#~ " let caller = get_caller_address();\n" -#~ " self\n" -#~ " .approve_helper(\n" -#~ " caller, spender, self.allowances.read((caller, " -#~ "spender)) - subtracted_value\n" -#~ " );\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " #[generate_trait]\n" -#~ " impl InternalImpl of InternalTrait {\n" -#~ " fn _transfer(\n" -#~ " ref self: ContractState,\n" -#~ " sender: ContractAddress,\n" -#~ " recipient: ContractAddress,\n" -#~ " amount: felt252\n" -#~ " ) {\n" -#~ " assert(!sender.is_zero(), Errors::TRANSFER_FROM_ZERO);\n" -#~ " assert(!recipient.is_zero(), Errors::TRANSFER_TO_ZERO);\n" -#~ " self.balances.write(sender, self.balances.read(sender) - " -#~ "amount);\n" -#~ " self.balances.write(recipient, self.balances.read(recipient) " -#~ "+ amount);\n" -#~ " self.emit(Transfer { from: sender, to: recipient, value: " -#~ "amount });\n" -#~ " }\n" -#~ "\n" -#~ " fn spend_allowance(\n" -#~ " ref self: ContractState,\n" -#~ " owner: ContractAddress,\n" -#~ " spender: ContractAddress,\n" -#~ " amount: felt252\n" -#~ " ) {\n" -#~ " let allowance = self.allowances.read((owner, spender));\n" -#~ " self.allowances.write((owner, spender), allowance - amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn approve_helper(\n" -#~ " ref self: ContractState,\n" -#~ " owner: ContractAddress,\n" -#~ " spender: ContractAddress,\n" -#~ " amount: felt252\n" -#~ " ) {\n" -#~ " assert(!spender.is_zero(), Errors::APPROVE_TO_ZERO);\n" -#~ " self.allowances.write((owner, spender), amount);\n" -#~ " self.emit(Approval { owner, spender, value: amount });\n" -#~ " }\n" -#~ "\n" -#~ " fn mint(ref self: ContractState, recipient: ContractAddress, " -#~ "amount: felt252) {\n" -#~ " assert(!recipient.is_zero(), Errors::MINT_TO_ZERO);\n" -#~ " let supply = self.total_supply.read() + amount; // What can " -#~ "go wrong here?\n" -#~ " self.total_supply.write(supply);\n" -#~ " let balance = self.balances.read(recipient) + amount;\n" -#~ " self.balances.write(recipient, amount);\n" -#~ " self\n" -#~ " .emit(\n" -#~ " Event::Transfer(\n" -#~ " Transfer {\n" -#~ " from: contract_address_const::<0>(), to: " -#~ "recipient, value: amount\n" -#~ " }\n" -#~ " )\n" -#~ " );\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::contract]\n" -#~ "mod erc20 {\n" -#~ " use zeroable::Zeroable;\n" -#~ " use starknet::get_caller_address;\n" -#~ " use starknet::contract_address_const;\n" -#~ " use starknet::ContractAddress;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " name: felt252,\n" -#~ " symbol: felt252,\n" -#~ " decimals: u8,\n" -#~ " total_supply: felt252,\n" -#~ " balances: LegacyMap::,\n" -#~ " allowances: LegacyMap::<(ContractAddress, ContractAddress), " -#~ "felt252>,\n" -#~ " }\n" -#~ "\n" -#~ " #[event]\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " enum Event {\n" -#~ " Transfer: Transfer,\n" -#~ " Approval: Approval,\n" -#~ " }\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " struct Transfer {\n" -#~ " from: ContractAddress,\n" -#~ " to: ContractAddress,\n" -#~ " value: felt252,\n" -#~ " }\n" -#~ " #[derive(Drop, starknet::Event)]\n" -#~ " struct Approval {\n" -#~ " owner: ContractAddress,\n" -#~ " spender: ContractAddress,\n" -#~ " value: felt252,\n" -#~ " }\n" -#~ "\n" -#~ " mod Errors {\n" -#~ " const APPROVE_FROM_ZERO: felt252 = 'ERC20: approve from 0';\n" -#~ " const APPROVE_TO_ZERO: felt252 = 'ERC20: approve to 0';\n" -#~ " const TRANSFER_FROM_ZERO: felt252 = 'ERC20: transfer from 0';\n" -#~ " const TRANSFER_TO_ZERO: felt252 = 'ERC20: transfer to 0';\n" -#~ " const BURN_FROM_ZERO: felt252 = 'ERC20: burn from 0';\n" -#~ " const MINT_TO_ZERO: felt252 = 'ERC20: mint to 0';\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(\n" -#~ " ref self: ContractState,\n" -#~ " recipient: ContractAddress,\n" -#~ " name: felt252,\n" -#~ " decimals: u8,\n" -#~ " initial_supply: felt252,\n" -#~ " symbol: felt252\n" -#~ " ) {\n" -#~ " self.name.write(name);\n" -#~ " self.symbol.write(symbol);\n" -#~ " self.decimals.write(decimals);\n" -#~ " self.mint(recipient, initial_supply);\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl IERC20Impl of super::IERC20 {\n" -#~ " fn get_name(self: @ContractState) -> felt252 {\n" -#~ " self.name.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn get_symbol(self: @ContractState) -> felt252 {\n" -#~ " self.symbol.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn get_decimals(self: @ContractState) -> u8 {\n" -#~ " self.decimals.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn get_total_supply(self: @ContractState) -> felt252 {\n" -#~ " self.total_supply.read()\n" -#~ " }\n" -#~ "\n" -#~ " fn balance_of(self: @ContractState, account: ContractAddress) -> " -#~ "felt252 {\n" -#~ " self.balances.read(account)\n" -#~ " }\n" -#~ "\n" -#~ " fn allowance(\n" -#~ " self: @ContractState, owner: ContractAddress, spender: " -#~ "ContractAddress\n" -#~ " ) -> felt252 {\n" -#~ " self.allowances.read((owner, spender))\n" -#~ " }\n" -#~ "\n" -#~ " fn transfer(ref self: ContractState, recipient: ContractAddress, " -#~ "amount: felt252) {\n" -#~ " let sender = get_caller_address();\n" -#~ " self._transfer(sender, recipient, amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn transfer_from(\n" -#~ " ref self: ContractState,\n" -#~ " sender: ContractAddress,\n" -#~ " recipient: ContractAddress,\n" -#~ " amount: felt252\n" -#~ " ) {\n" -#~ " let caller = get_caller_address();\n" -#~ " self.spend_allowance(sender, caller, amount);\n" -#~ " self._transfer(sender, recipient, amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn approve(ref self: ContractState, spender: ContractAddress, " -#~ "amount: felt252) {\n" -#~ " let caller = get_caller_address();\n" -#~ " self.approve_helper(caller, spender, amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn increase_allowance(\n" -#~ " ref self: ContractState, spender: ContractAddress, " -#~ "added_value: felt252\n" -#~ " ) {\n" -#~ " let caller = get_caller_address();\n" -#~ " self\n" -#~ " .approve_helper(\n" -#~ " caller, spender, self.allowances.read((caller, " -#~ "spender)) + added_value\n" -#~ " );\n" -#~ " }\n" -#~ "\n" -#~ " fn decrease_allowance(\n" -#~ " ref self: ContractState, spender: ContractAddress, " -#~ "subtracted_value: felt252\n" -#~ " ) {\n" -#~ " let caller = get_caller_address();\n" -#~ " self\n" -#~ " .approve_helper(\n" -#~ " caller, spender, self.allowances.read((caller, " -#~ "spender)) - subtracted_value\n" -#~ " );\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " #[generate_trait]\n" -#~ " impl InternalImpl of InternalTrait {\n" -#~ " fn _transfer(\n" -#~ " ref self: ContractState,\n" -#~ " sender: ContractAddress,\n" -#~ " recipient: ContractAddress,\n" -#~ " amount: felt252\n" -#~ " ) {\n" -#~ " assert(!sender.is_zero(), Errors::TRANSFER_FROM_ZERO);\n" -#~ " assert(!recipient.is_zero(), Errors::TRANSFER_TO_ZERO);\n" -#~ " self.balances.write(sender, self.balances.read(sender) - " -#~ "amount);\n" -#~ " self.balances.write(recipient, self.balances.read(recipient) " -#~ "+ amount);\n" -#~ " self.emit(Transfer { from: sender, to: recipient, value: " -#~ "amount });\n" -#~ " }\n" -#~ "\n" -#~ " fn spend_allowance(\n" -#~ " ref self: ContractState,\n" -#~ " owner: ContractAddress,\n" -#~ " spender: ContractAddress,\n" -#~ " amount: felt252\n" -#~ " ) {\n" -#~ " let allowance = self.allowances.read((owner, spender));\n" -#~ " self.allowances.write((owner, spender), allowance - amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn approve_helper(\n" -#~ " ref self: ContractState,\n" -#~ " owner: ContractAddress,\n" -#~ " spender: ContractAddress,\n" -#~ " amount: felt252\n" -#~ " ) {\n" -#~ " assert(!spender.is_zero(), Errors::APPROVE_TO_ZERO);\n" -#~ " self.allowances.write((owner, spender), amount);\n" -#~ " self.emit(Approval { owner, spender, value: amount });\n" -#~ " }\n" -#~ "\n" -#~ " fn mint(ref self: ContractState, recipient: ContractAddress, " -#~ "amount: felt252) {\n" -#~ " assert(!recipient.is_zero(), Errors::MINT_TO_ZERO);\n" -#~ " let supply = self.total_supply.read() + amount; // What can " -#~ "go wrong here?\n" -#~ " self.total_supply.write(supply);\n" -#~ " let balance = self.balances.read(recipient) + amount;\n" -#~ " self.balances.write(recipient, amount);\n" -#~ " self\n" -#~ " .emit(\n" -#~ " Event::Transfer(\n" -#~ " Transfer {\n" -#~ " from: contract_address_const::<0>(), to: " -#~ "recipient, value: amount\n" -#~ " }\n" -#~ " )\n" -#~ " );\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "
Last change: 2023-10-24
" -#~ msgstr "
Last change: 2023-10-24
" - -#~ msgid "# Constant Product AMM" -#~ msgstr "# 恒定乘积自动做市商" - -#~ msgid "" -#~ "```rust\n" -#~ "use starknet::ContractAddress;\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IConstantProductAmm {\n" -#~ " fn swap(ref self: TContractState, token_in: ContractAddress, " -#~ "amount_in: u256) -> u256;\n" -#~ " fn add_liquidity(ref self: TContractState, amount0: u256, amount1: " -#~ "u256) -> u256;\n" -#~ " fn remove_liquidity(ref self: TContractState, shares: u256) -> (u256, " -#~ "u256);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod ConstantProductAmm {\n" -#~ " use core::traits::Into;\n" -#~ " use openzeppelin::token::erc20::interface::{IERC20Dispatcher, " -#~ "IERC20DispatcherTrait};\n" -#~ " use starknet::{\n" -#~ " ContractAddress, get_caller_address, get_contract_address, " -#~ "contract_address_const\n" -#~ " };\n" -#~ " use integer::u256_sqrt;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " token0: IERC20Dispatcher,\n" -#~ " token1: IERC20Dispatcher,\n" -#~ " reserve0: u256,\n" -#~ " reserve1: u256,\n" -#~ " total_supply: u256,\n" -#~ " balance_of: LegacyMap::,\n" -#~ " // Fee 0 - 1000 (0% - 100%, 1 decimal places)\n" -#~ " // E.g. 3 = 0.3%\n" -#~ " fee: u16,\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(\n" -#~ " ref self: ContractState, token0: ContractAddress, token1: " -#~ "ContractAddress, fee: u16\n" -#~ " ) {\n" -#~ " // assert(fee <= 1000, 'fee > 1000');\n" -#~ " self.token0.write(IERC20Dispatcher { contract_address: " -#~ "token0 });\n" -#~ " self.token1.write(IERC20Dispatcher { contract_address: " -#~ "token1 });\n" -#~ " self.fee.write(fee);\n" -#~ " }\n" -#~ "\n" -#~ " #[generate_trait]\n" -#~ " impl PrivateFunctions of PrivateFunctionsTrait {\n" -#~ " fn _mint(ref self: ContractState, to: ContractAddress, amount: " -#~ "u256) {\n" -#~ " self.balance_of.write(to, self.balance_of.read(to) + " -#~ "amount);\n" -#~ " self.total_supply.write(self.total_supply.read() + amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn _burn(ref self: ContractState, from: ContractAddress, amount: " -#~ "u256) {\n" -#~ " self.balance_of.write(from, self.balance_of.read(from) - " -#~ "amount);\n" -#~ " self.total_supply.write(self.total_supply.read() - amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn _update(ref self: ContractState, reserve0: u256, reserve1: " -#~ "u256) {\n" -#~ " self.reserve0.write(reserve0);\n" -#~ " self.reserve1.write(reserve1);\n" -#~ " }\n" -#~ "\n" -#~ " #[inline(always)]\n" -#~ " fn select_token(self: @ContractState, token: ContractAddress) -> " -#~ "bool {\n" -#~ " assert(\n" -#~ " token == self.token0.read().contract_address\n" -#~ " || token == self.token1.read().contract_address,\n" -#~ " 'invalid token'\n" -#~ " );\n" -#~ " token == self.token0.read().contract_address\n" -#~ " }\n" -#~ "\n" -#~ " #[inline(always)]\n" -#~ " fn min(x: u256, y: u256) -> u256 {\n" -#~ " if (x <= y) {\n" -#~ " x\n" -#~ " } else {\n" -#~ " y\n" -#~ " }\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ConstantProductAmm of super::IConstantProductAmm " -#~ "{\n" -#~ " fn swap(ref self: ContractState, token_in: ContractAddress, " -#~ "amount_in: u256) -> u256 {\n" -#~ " assert(amount_in > 0, 'amount in = 0');\n" -#~ " let is_token0: bool = self.select_token(token_in);\n" -#~ "\n" -#~ " let (token0, token1): (IERC20Dispatcher, IERC20Dispatcher) = " -#~ "(\n" -#~ " self.token0.read(), self.token1.read()\n" -#~ " );\n" -#~ " let (reserve0, reserve1): (u256, u256) = (self.reserve0." -#~ "read(), self.reserve1.read());\n" -#~ " let (\n" -#~ " token_in, token_out, reserve_in, reserve_out\n" -#~ " ): (IERC20Dispatcher, IERC20Dispatcher, u256, u256) =\n" -#~ " if (is_token0) {\n" -#~ " (token0, token1, reserve0, reserve1)\n" -#~ " } else {\n" -#~ " (token1, token0, reserve1, reserve0)\n" -#~ " };\n" -#~ "\n" -#~ " let caller = get_caller_address();\n" -#~ " let this = get_contract_address();\n" -#~ " token_in.transfer_from(caller, this, amount_in);\n" -#~ "\n" -#~ " // How much dy for dx?\n" -#~ " // xy = k\n" -#~ " // (x + dx)(y - dy) = k\n" -#~ " // y - dy = k / (x + dx)\n" -#~ " // y - k / (x + dx) = dy\n" -#~ " // y - xy / (x + dx) = dy\n" -#~ " // (yx + ydx - xy) / (x + dx) = dy\n" -#~ " // ydx / (x + dx) = dy\n" -#~ "\n" -#~ " let amount_in_with_fee = (amount_in * (1000 - self.fee.read()." -#~ "into()) / 1000);\n" -#~ " let amount_out = (reserve_out * amount_in_with_fee) / " -#~ "(reserve_in + amount_in_with_fee);\n" -#~ "\n" -#~ " token_out.transfer(caller, amount_out);\n" -#~ "\n" -#~ " self._update(self.token0.read().balance_of(this), self.token1." -#~ "read().balance_of(this));\n" -#~ " amount_out\n" -#~ " }\n" -#~ "\n" -#~ " fn add_liquidity(ref self: ContractState, amount0: u256, amount1: " -#~ "u256) -> u256 {\n" -#~ " let caller = get_caller_address();\n" -#~ " let this = get_contract_address();\n" -#~ " let (token0, token1): (IERC20Dispatcher, IERC20Dispatcher) = " -#~ "(\n" -#~ " self.token0.read(), self.token1.read()\n" -#~ " );\n" -#~ "\n" -#~ " token0.transfer_from(caller, this, amount0);\n" -#~ " token1.transfer_from(caller, this, amount1);\n" -#~ "\n" -#~ " // How much dx, dy to add?\n" -#~ " //\n" -#~ " // xy = k\n" -#~ " // (x + dx)(y + dy) = k'\n" -#~ " //\n" -#~ " // No price change, before and after adding liquidity\n" -#~ " // x / y = (x + dx) / (y + dy)\n" -#~ " //\n" -#~ " // x(y + dy) = y(x + dx)\n" -#~ " // x * dy = y * dx\n" -#~ " //\n" -#~ " // x / y = dx / dy\n" -#~ " // dy = y / x * dx\n" -#~ "\n" -#~ " let (reserve0, reserve1): (u256, u256) = (self.reserve0." -#~ "read(), self.reserve1.read());\n" -#~ " if (reserve0 > 0 || reserve1 > 0) {\n" -#~ " assert(reserve0 * amount1 == reserve1 * amount0, 'x / y !" -#~ "= dx / dy');\n" -#~ " }\n" -#~ "\n" -#~ " // How much shares to mint?\n" -#~ " //\n" -#~ " // f(x, y) = value of liquidity\n" -#~ " // We will define f(x, y) = sqrt(xy)\n" -#~ " //\n" -#~ " // L0 = f(x, y)\n" -#~ " // L1 = f(x + dx, y + dy)\n" -#~ " // T = total shares\n" -#~ " // s = shares to mint\n" -#~ " //\n" -#~ " // Total shares should increase proportional to increase in " -#~ "liquidity\n" -#~ " // L1 / L0 = (T + s) / T\n" -#~ " //\n" -#~ " // L1 * T = L0 * (T + s)\n" -#~ " //\n" -#~ " // (L1 - L0) * T / L0 = s\n" -#~ "\n" -#~ " // Claim\n" -#~ " // (L1 - L0) / L0 = dx / x = dy / y\n" -#~ " //\n" -#~ " // Proof\n" -#~ " // --- Equation 1 ---\n" -#~ " // (L1 - L0) / L0 = (sqrt((x + dx)(y + dy)) - sqrt(xy)) / " -#~ "sqrt(xy)\n" -#~ " //\n" -#~ " // dx / dy = x / y so replace dy = dx * y / x\n" -#~ " //\n" -#~ " // --- Equation 2 ---\n" -#~ " // Equation 1 = (sqrt(xy + 2ydx + dx^2 * y / x) - sqrt(xy)) / " -#~ "sqrt(xy)\n" -#~ " //\n" -#~ " // Multiply by sqrt(x) / sqrt(x)\n" -#~ " // Equation 2 = (sqrt(x^2y + 2xydx + dx^2 * y) - " -#~ "sqrt(x^2y)) / sqrt(x^2y)\n" -#~ " // = (sqrt(y)(sqrt(x^2 + 2xdx + dx^2) - " -#~ "sqrt(x^2)) / (sqrt(y)sqrt(x^2))\n" -#~ " // sqrt(y) on top and bottom cancels out\n" -#~ " //\n" -#~ " // --- Equation 3 ---\n" -#~ " // Equation 2 = (sqrt(x^2 + 2xdx + dx^2) - sqrt(x^2)) / " -#~ "(sqrt(x^2)\n" -#~ " // = (sqrt((x + dx)^2) - sqrt(x^2)) / sqrt(x^2)\n" -#~ " // = ((x + dx) - x) / x\n" -#~ " // = dx / x\n" -#~ " // Since dx / dy = x / y,\n" -#~ " // dx / x = dy / y\n" -#~ " //\n" -#~ " // Finally\n" -#~ " // (L1 - L0) / L0 = dx / x = dy / y\n" -#~ "\n" -#~ " let total_supply = self.total_supply.read();\n" -#~ " let shares = if (total_supply == 0) {\n" -#~ " u256_sqrt(amount0 * amount1).into()\n" -#~ " } else {\n" -#~ " PrivateFunctions::min(\n" -#~ " amount0 * total_supply / reserve0, amount1 * " -#~ "total_supply / reserve1\n" -#~ " )\n" -#~ " };\n" -#~ " assert(shares > 0, 'shares = 0');\n" -#~ " self._mint(caller, shares);\n" -#~ "\n" -#~ " self._update(self.token0.read().balance_of(this), self.token1." -#~ "read().balance_of(this));\n" -#~ " shares\n" -#~ " }\n" -#~ "\n" -#~ " fn remove_liquidity(ref self: ContractState, shares: u256) -> " -#~ "(u256, u256) {\n" -#~ " let caller = get_caller_address();\n" -#~ " let this = get_contract_address();\n" -#~ " let (token0, token1): (IERC20Dispatcher, IERC20Dispatcher) = " -#~ "(\n" -#~ " self.token0.read(), self.token1.read()\n" -#~ " );\n" -#~ "\n" -#~ " // Claim\n" -#~ " // dx, dy = amount of liquidity to remove\n" -#~ " // dx = s / T * x\n" -#~ " // dy = s / T * y\n" -#~ " //\n" -#~ " // Proof\n" -#~ " // Let's find dx, dy such that\n" -#~ " // v / L = s / T\n" -#~ " //\n" -#~ " // where\n" -#~ " // v = f(dx, dy) = sqrt(dxdy)\n" -#~ " // L = total liquidity = sqrt(xy)\n" -#~ " // s = shares\n" -#~ " // T = total supply\n" -#~ " //\n" -#~ " // --- Equation 1 ---\n" -#~ " // v = s / T * L\n" -#~ " // sqrt(dxdy) = s / T * sqrt(xy)\n" -#~ " //\n" -#~ " // Amount of liquidity to remove must not change price so\n" -#~ " // dx / dy = x / y\n" -#~ " //\n" -#~ " // replace dy = dx * y / x\n" -#~ " // sqrt(dxdy) = sqrt(dx * dx * y / x) = dx * sqrt(y / x)\n" -#~ " //\n" -#~ " // Divide both sides of Equation 1 with sqrt(y / x)\n" -#~ " // dx = s / T * sqrt(xy) / sqrt(y / x)\n" -#~ " // = s / T * sqrt(x^2) = s / T * x\n" -#~ " //\n" -#~ " // Likewise\n" -#~ " // dy = s / T * y\n" -#~ "\n" -#~ " // bal0 >= reserve0\n" -#~ " // bal1 >= reserve1\n" -#~ " let (bal0, bal1): (u256, u256) = (token0.balance_of(this), " -#~ "token1.balance_of(this));\n" -#~ "\n" -#~ " let total_supply = self.total_supply.read();\n" -#~ " let (amount0, amount1): (u256, u256) = (\n" -#~ " (shares * bal0) / total_supply, (shares * bal1) / " -#~ "total_supply\n" -#~ " );\n" -#~ " assert(amount0 > 0 && amount1 > 0, 'amount0 or amount1 = " -#~ "0');\n" -#~ "\n" -#~ " self._burn(caller, shares);\n" -#~ " self._update(bal0 - amount0, bal1 - amount1);\n" -#~ "\n" -#~ " token0.transfer(caller, amount0);\n" -#~ " token1.transfer(caller, amount1);\n" -#~ " (amount0, amount1)\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "use starknet::ContractAddress;\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IConstantProductAmm {\n" -#~ " fn swap(ref self: TContractState, token_in: ContractAddress, " -#~ "amount_in: u256) -> u256;\n" -#~ " fn add_liquidity(ref self: TContractState, amount0: u256, amount1: " -#~ "u256) -> u256;\n" -#~ " fn remove_liquidity(ref self: TContractState, shares: u256) -> (u256, " -#~ "u256);\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod ConstantProductAmm {\n" -#~ " use core::traits::Into;\n" -#~ " use openzeppelin::token::erc20::interface::{IERC20Dispatcher, " -#~ "IERC20DispatcherTrait};\n" -#~ " use starknet::{\n" -#~ " ContractAddress, get_caller_address, get_contract_address, " -#~ "contract_address_const\n" -#~ " };\n" -#~ " use integer::u256_sqrt;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " token0: IERC20Dispatcher,\n" -#~ " token1: IERC20Dispatcher,\n" -#~ " reserve0: u256,\n" -#~ " reserve1: u256,\n" -#~ " total_supply: u256,\n" -#~ " balance_of: LegacyMap::,\n" -#~ " // Fee 0 - 1000 (0% - 100%, 1 decimal places)\n" -#~ " // E.g. 3 = 0.3%\n" -#~ " fee: u16,\n" -#~ " }\n" -#~ "\n" -#~ " #[constructor]\n" -#~ " fn constructor(\n" -#~ " ref self: ContractState, token0: ContractAddress, token1: " -#~ "ContractAddress, fee: u16\n" -#~ " ) {\n" -#~ " // assert(fee <= 1000, 'fee > 1000');\n" -#~ " self.token0.write(IERC20Dispatcher { contract_address: " -#~ "token0 });\n" -#~ " self.token1.write(IERC20Dispatcher { contract_address: " -#~ "token1 });\n" -#~ " self.fee.write(fee);\n" -#~ " }\n" -#~ "\n" -#~ " #[generate_trait]\n" -#~ " impl PrivateFunctions of PrivateFunctionsTrait {\n" -#~ " fn _mint(ref self: ContractState, to: ContractAddress, amount: " -#~ "u256) {\n" -#~ " self.balance_of.write(to, self.balance_of.read(to) + " -#~ "amount);\n" -#~ " self.total_supply.write(self.total_supply.read() + amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn _burn(ref self: ContractState, from: ContractAddress, amount: " -#~ "u256) {\n" -#~ " self.balance_of.write(from, self.balance_of.read(from) - " -#~ "amount);\n" -#~ " self.total_supply.write(self.total_supply.read() - amount);\n" -#~ " }\n" -#~ "\n" -#~ " fn _update(ref self: ContractState, reserve0: u256, reserve1: " -#~ "u256) {\n" -#~ " self.reserve0.write(reserve0);\n" -#~ " self.reserve1.write(reserve1);\n" -#~ " }\n" -#~ "\n" -#~ " #[inline(always)]\n" -#~ " fn select_token(self: @ContractState, token: ContractAddress) -> " -#~ "bool {\n" -#~ " assert(\n" -#~ " token == self.token0.read().contract_address\n" -#~ " || token == self.token1.read().contract_address,\n" -#~ " 'invalid token'\n" -#~ " );\n" -#~ " token == self.token0.read().contract_address\n" -#~ " }\n" -#~ "\n" -#~ " #[inline(always)]\n" -#~ " fn min(x: u256, y: u256) -> u256 {\n" -#~ " if (x <= y) {\n" -#~ " x\n" -#~ " } else {\n" -#~ " y\n" -#~ " }\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ConstantProductAmm of super::IConstantProductAmm " -#~ "{\n" -#~ " fn swap(ref self: ContractState, token_in: ContractAddress, " -#~ "amount_in: u256) -> u256 {\n" -#~ " assert(amount_in > 0, 'amount in = 0');\n" -#~ " let is_token0: bool = self.select_token(token_in);\n" -#~ "\n" -#~ " let (token0, token1): (IERC20Dispatcher, IERC20Dispatcher) = " -#~ "(\n" -#~ " self.token0.read(), self.token1.read()\n" -#~ " );\n" -#~ " let (reserve0, reserve1): (u256, u256) = (self.reserve0." -#~ "read(), self.reserve1.read());\n" -#~ " let (\n" -#~ " token_in, token_out, reserve_in, reserve_out\n" -#~ " ): (IERC20Dispatcher, IERC20Dispatcher, u256, u256) =\n" -#~ " if (is_token0) {\n" -#~ " (token0, token1, reserve0, reserve1)\n" -#~ " } else {\n" -#~ " (token1, token0, reserve1, reserve0)\n" -#~ " };\n" -#~ "\n" -#~ " let caller = get_caller_address();\n" -#~ " let this = get_contract_address();\n" -#~ " token_in.transfer_from(caller, this, amount_in);\n" -#~ "\n" -#~ " // How much dy for dx?\n" -#~ " // xy = k\n" -#~ " // (x + dx)(y - dy) = k\n" -#~ " // y - dy = k / (x + dx)\n" -#~ " // y - k / (x + dx) = dy\n" -#~ " // y - xy / (x + dx) = dy\n" -#~ " // (yx + ydx - xy) / (x + dx) = dy\n" -#~ " // ydx / (x + dx) = dy\n" -#~ "\n" -#~ " let amount_in_with_fee = (amount_in * (1000 - self.fee.read()." -#~ "into()) / 1000);\n" -#~ " let amount_out = (reserve_out * amount_in_with_fee) / " -#~ "(reserve_in + amount_in_with_fee);\n" -#~ "\n" -#~ " token_out.transfer(caller, amount_out);\n" -#~ "\n" -#~ " self._update(self.token0.read().balance_of(this), self.token1." -#~ "read().balance_of(this));\n" -#~ " amount_out\n" -#~ " }\n" -#~ "\n" -#~ " fn add_liquidity(ref self: ContractState, amount0: u256, amount1: " -#~ "u256) -> u256 {\n" -#~ " let caller = get_caller_address();\n" -#~ " let this = get_contract_address();\n" -#~ " let (token0, token1): (IERC20Dispatcher, IERC20Dispatcher) = " -#~ "(\n" -#~ " self.token0.read(), self.token1.read()\n" -#~ " );\n" -#~ "\n" -#~ " token0.transfer_from(caller, this, amount0);\n" -#~ " token1.transfer_from(caller, this, amount1);\n" -#~ "\n" -#~ " // How much dx, dy to add?\n" -#~ " //\n" -#~ " // xy = k\n" -#~ " // (x + dx)(y + dy) = k'\n" -#~ " //\n" -#~ " // No price change, before and after adding liquidity\n" -#~ " // x / y = (x + dx) / (y + dy)\n" -#~ " //\n" -#~ " // x(y + dy) = y(x + dx)\n" -#~ " // x * dy = y * dx\n" -#~ " //\n" -#~ " // x / y = dx / dy\n" -#~ " // dy = y / x * dx\n" -#~ "\n" -#~ " let (reserve0, reserve1): (u256, u256) = (self.reserve0." -#~ "read(), self.reserve1.read());\n" -#~ " if (reserve0 > 0 || reserve1 > 0) {\n" -#~ " assert(reserve0 * amount1 == reserve1 * amount0, 'x / y !" -#~ "= dx / dy');\n" -#~ " }\n" -#~ "\n" -#~ " // How much shares to mint?\n" -#~ " //\n" -#~ " // f(x, y) = value of liquidity\n" -#~ " // We will define f(x, y) = sqrt(xy)\n" -#~ " //\n" -#~ " // L0 = f(x, y)\n" -#~ " // L1 = f(x + dx, y + dy)\n" -#~ " // T = total shares\n" -#~ " // s = shares to mint\n" -#~ " //\n" -#~ " // Total shares should increase proportional to increase in " -#~ "liquidity\n" -#~ " // L1 / L0 = (T + s) / T\n" -#~ " //\n" -#~ " // L1 * T = L0 * (T + s)\n" -#~ " //\n" -#~ " // (L1 - L0) * T / L0 = s\n" -#~ "\n" -#~ " // Claim\n" -#~ " // (L1 - L0) / L0 = dx / x = dy / y\n" -#~ " //\n" -#~ " // Proof\n" -#~ " // --- Equation 1 ---\n" -#~ " // (L1 - L0) / L0 = (sqrt((x + dx)(y + dy)) - sqrt(xy)) / " -#~ "sqrt(xy)\n" -#~ " //\n" -#~ " // dx / dy = x / y so replace dy = dx * y / x\n" -#~ " //\n" -#~ " // --- Equation 2 ---\n" -#~ " // Equation 1 = (sqrt(xy + 2ydx + dx^2 * y / x) - sqrt(xy)) / " -#~ "sqrt(xy)\n" -#~ " //\n" -#~ " // Multiply by sqrt(x) / sqrt(x)\n" -#~ " // Equation 2 = (sqrt(x^2y + 2xydx + dx^2 * y) - " -#~ "sqrt(x^2y)) / sqrt(x^2y)\n" -#~ " // = (sqrt(y)(sqrt(x^2 + 2xdx + dx^2) - " -#~ "sqrt(x^2)) / (sqrt(y)sqrt(x^2))\n" -#~ " // sqrt(y) on top and bottom cancels out\n" -#~ " //\n" -#~ " // --- Equation 3 ---\n" -#~ " // Equation 2 = (sqrt(x^2 + 2xdx + dx^2) - sqrt(x^2)) / " -#~ "(sqrt(x^2)\n" -#~ " // = (sqrt((x + dx)^2) - sqrt(x^2)) / sqrt(x^2)\n" -#~ " // = ((x + dx) - x) / x\n" -#~ " // = dx / x\n" -#~ " // Since dx / dy = x / y,\n" -#~ " // dx / x = dy / y\n" -#~ " //\n" -#~ " // Finally\n" -#~ " // (L1 - L0) / L0 = dx / x = dy / y\n" -#~ "\n" -#~ " let total_supply = self.total_supply.read();\n" -#~ " let shares = if (total_supply == 0) {\n" -#~ " u256_sqrt(amount0 * amount1).into()\n" -#~ " } else {\n" -#~ " PrivateFunctions::min(\n" -#~ " amount0 * total_supply / reserve0, amount1 * " -#~ "total_supply / reserve1\n" -#~ " )\n" -#~ " };\n" -#~ " assert(shares > 0, 'shares = 0');\n" -#~ " self._mint(caller, shares);\n" -#~ "\n" -#~ " self._update(self.token0.read().balance_of(this), self.token1." -#~ "read().balance_of(this));\n" -#~ " shares\n" -#~ " }\n" -#~ "\n" -#~ " fn remove_liquidity(ref self: ContractState, shares: u256) -> " -#~ "(u256, u256) {\n" -#~ " let caller = get_caller_address();\n" -#~ " let this = get_contract_address();\n" -#~ " let (token0, token1): (IERC20Dispatcher, IERC20Dispatcher) = " -#~ "(\n" -#~ " self.token0.read(), self.token1.read()\n" -#~ " );\n" -#~ "\n" -#~ " // Claim\n" -#~ " // dx, dy = amount of liquidity to remove\n" -#~ " // dx = s / T * x\n" -#~ " // dy = s / T * y\n" -#~ " //\n" -#~ " // Proof\n" -#~ " // Let's find dx, dy such that\n" -#~ " // v / L = s / T\n" -#~ " //\n" -#~ " // where\n" -#~ " // v = f(dx, dy) = sqrt(dxdy)\n" -#~ " // L = total liquidity = sqrt(xy)\n" -#~ " // s = shares\n" -#~ " // T = total supply\n" -#~ " //\n" -#~ " // --- Equation 1 ---\n" -#~ " // v = s / T * L\n" -#~ " // sqrt(dxdy) = s / T * sqrt(xy)\n" -#~ " //\n" -#~ " // Amount of liquidity to remove must not change price so\n" -#~ " // dx / dy = x / y\n" -#~ " //\n" -#~ " // replace dy = dx * y / x\n" -#~ " // sqrt(dxdy) = sqrt(dx * dx * y / x) = dx * sqrt(y / x)\n" -#~ " //\n" -#~ " // Divide both sides of Equation 1 with sqrt(y / x)\n" -#~ " // dx = s / T * sqrt(xy) / sqrt(y / x)\n" -#~ " // = s / T * sqrt(x^2) = s / T * x\n" -#~ " //\n" -#~ " // Likewise\n" -#~ " // dy = s / T * y\n" -#~ "\n" -#~ " // bal0 >= reserve0\n" -#~ " // bal1 >= reserve1\n" -#~ " let (bal0, bal1): (u256, u256) = (token0.balance_of(this), " -#~ "token1.balance_of(this));\n" -#~ "\n" -#~ " let total_supply = self.total_supply.read();\n" -#~ " let (amount0, amount1): (u256, u256) = (\n" -#~ " (shares * bal0) / total_supply, (shares * bal1) / " -#~ "total_supply\n" -#~ " );\n" -#~ " assert(amount0 > 0 && amount1 > 0, 'amount0 or amount1 = " -#~ "0');\n" -#~ "\n" -#~ " self._burn(caller, shares);\n" -#~ " self._update(bal0 - amount0, bal1 - amount1);\n" -#~ "\n" -#~ " token0.transfer(caller, amount0);\n" -#~ " token1.transfer(caller, amount1);\n" -#~ " (amount0, amount1)\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Writing to any storage slot" -#~ msgstr "# 写入任何存储槽" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IWriteToAnySlots {\n" -#~ " fn write_slot(ref self: TContractState, value: u32);\n" -#~ " fn read_slot(self: @TContractState) -> u32;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod WriteToAnySlot {\n" -#~ " use starknet::syscalls::{storage_read_syscall, " -#~ "storage_write_syscall};\n" -#~ " use starknet::SyscallResultTrait;\n" -#~ " use poseidon::poseidon_hash_span;\n" -#~ " use starknet::storage_access::Felt252TryIntoStorageAddress;\n" -#~ " use starknet::StorageAddress;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " const SLOT_NAME: felt252 = 'test_slot';\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl WriteToAnySlot of super::IWriteToAnySlots {\n" -#~ " fn write_slot(ref self: ContractState, value: u32) {\n" -#~ " storage_write_syscall(0, get_address_from_name(SLOT_NAME), " -#~ "value.into());\n" -#~ " }\n" -#~ "\n" -#~ " fn read_slot(self: @ContractState) -> u32 {\n" -#~ " storage_read_syscall(0, get_address_from_name(SLOT_NAME))\n" -#~ " .unwrap_syscall()\n" -#~ " .try_into()\n" -#~ " .unwrap()\n" -#~ " }\n" -#~ " }\n" -#~ " fn get_address_from_name(variable_name: felt252) -> StorageAddress {\n" -#~ " let mut data: Array = ArrayTrait::new();\n" -#~ " data.append(variable_name);\n" -#~ " let hashed_name: felt252 = poseidon_hash_span(data.span());\n" -#~ " let MASK_250: u256 = " -#~ "0x03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;\n" -#~ " // By taking the 250 least significant bits of the hash output, " -#~ "we get a valid 250bits storage address.\n" -#~ " let result: felt252 = (hashed_name.into() & MASK_250).try_into()." -#~ "unwrap();\n" -#~ " let result: StorageAddress = result.try_into().unwrap();\n" -#~ " result\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IWriteToAnySlots {\n" -#~ " fn write_slot(ref self: TContractState, value: u32);\n" -#~ " fn read_slot(self: @TContractState) -> u32;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod WriteToAnySlot {\n" -#~ " use starknet::syscalls::{storage_read_syscall, " -#~ "storage_write_syscall};\n" -#~ " use starknet::SyscallResultTrait;\n" -#~ " use poseidon::poseidon_hash_span;\n" -#~ " use starknet::storage_access::Felt252TryIntoStorageAddress;\n" -#~ " use starknet::StorageAddress;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " const SLOT_NAME: felt252 = 'test_slot';\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl WriteToAnySlot of super::IWriteToAnySlots {\n" -#~ " fn write_slot(ref self: ContractState, value: u32) {\n" -#~ " storage_write_syscall(0, get_address_from_name(SLOT_NAME), " -#~ "value.into());\n" -#~ " }\n" -#~ "\n" -#~ " fn read_slot(self: @ContractState) -> u32 {\n" -#~ " storage_read_syscall(0, get_address_from_name(SLOT_NAME))\n" -#~ " .unwrap_syscall()\n" -#~ " .try_into()\n" -#~ " .unwrap()\n" -#~ " }\n" -#~ " }\n" -#~ " fn get_address_from_name(variable_name: felt252) -> StorageAddress {\n" -#~ " let mut data: Array = ArrayTrait::new();\n" -#~ " data.append(variable_name);\n" -#~ " let hashed_name: felt252 = poseidon_hash_span(data.span());\n" -#~ " let MASK_250: u256 = " -#~ "0x03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;\n" -#~ " // By taking the 250 least significant bits of the hash output, " -#~ "we get a valid 250bits storage address.\n" -#~ " let result: felt252 = (hashed_name.into() & MASK_250).try_into()." -#~ "unwrap();\n" -#~ " let result: StorageAddress = result.try_into().unwrap();\n" -#~ " result\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Storing Arrays" -#~ msgstr "# 存储数组" - -#~ msgid "" -#~ "```rust\n" -#~ "impl StoreFelt252Array of Store> {\n" -#~ " fn read(address_domain: u32, base: StorageBaseAddress) -> " -#~ "SyscallResult> {\n" -#~ " StoreFelt252Array::read_at_offset(address_domain, base, 0)\n" -#~ " }\n" -#~ "\n" -#~ " fn write(\n" -#~ " address_domain: u32, base: StorageBaseAddress, value: " -#~ "Array\n" -#~ " ) -> SyscallResult<()> {\n" -#~ " StoreFelt252Array::write_at_offset(address_domain, base, 0, " -#~ "value)\n" -#~ " }\n" -#~ "\n" -#~ " fn read_at_offset(\n" -#~ " address_domain: u32, base: StorageBaseAddress, mut offset: u8\n" -#~ " ) -> SyscallResult> {\n" -#~ " let mut arr: Array = ArrayTrait::new();\n" -#~ "\n" -#~ " // Read the stored array's length. If the length is superior to " -#~ "255, the read will fail.\n" -#~ " let len: u8 = Store::::read_at_offset(address_domain, base, " -#~ "offset)\n" -#~ " .expect('Storage Span too large');\n" -#~ " offset += 1;\n" -#~ "\n" -#~ " // Sequentially read all stored elements and append them to the " -#~ "array.\n" -#~ " let exit = len + offset;\n" -#~ " loop {\n" -#~ " if offset >= exit {\n" -#~ " break;\n" -#~ " }\n" -#~ "\n" -#~ " let value = Store::::read_at_offset(address_domain, " -#~ "base, offset).unwrap();\n" -#~ " arr.append(value);\n" -#~ " offset += Store::::size();\n" -#~ " };\n" -#~ "\n" -#~ " // Return the array.\n" -#~ " Result::Ok(arr)\n" -#~ " }\n" -#~ "\n" -#~ " fn write_at_offset(\n" -#~ " address_domain: u32, base: StorageBaseAddress, mut offset: u8, " -#~ "mut value: Array\n" -#~ " ) -> SyscallResult<()> {\n" -#~ " // // Store the length of the array in the first storage slot.\n" -#~ " let len: u8 = value.len().try_into().expect('Storage - Span too " -#~ "large');\n" -#~ " Store::::write_at_offset(address_domain, base, offset, len);\n" -#~ " offset += 1;\n" -#~ "\n" -#~ " // Store the array elements sequentially\n" -#~ " loop {\n" -#~ " match value.pop_front() {\n" -#~ " Option::Some(element) => {\n" -#~ " Store::::write_at_offset(address_domain, " -#~ "base, offset, element);\n" -#~ " offset += Store::::size();\n" -#~ " },\n" -#~ " Option::None(_) => { break Result::Ok(()); }\n" -#~ " };\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " fn size() -> u8 {\n" -#~ " 255 * Store::::size()\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "impl StoreFelt252Array of Store> {\n" -#~ " fn read(address_domain: u32, base: StorageBaseAddress) -> " -#~ "SyscallResult> {\n" -#~ " StoreFelt252Array::read_at_offset(address_domain, base, 0)\n" -#~ " }\n" -#~ "\n" -#~ " fn write(\n" -#~ " address_domain: u32, base: StorageBaseAddress, value: " -#~ "Array\n" -#~ " ) -> SyscallResult<()> {\n" -#~ " StoreFelt252Array::write_at_offset(address_domain, base, 0, " -#~ "value)\n" -#~ " }\n" -#~ "\n" -#~ " fn read_at_offset(\n" -#~ " address_domain: u32, base: StorageBaseAddress, mut offset: u8\n" -#~ " ) -> SyscallResult> {\n" -#~ " let mut arr: Array = ArrayTrait::new();\n" -#~ "\n" -#~ " // Read the stored array's length. If the length is superior to " -#~ "255, the read will fail.\n" -#~ " let len: u8 = Store::::read_at_offset(address_domain, base, " -#~ "offset)\n" -#~ " .expect('Storage Span too large');\n" -#~ " offset += 1;\n" -#~ "\n" -#~ " // Sequentially read all stored elements and append them to the " -#~ "array.\n" -#~ " let exit = len + offset;\n" -#~ " loop {\n" -#~ " if offset >= exit {\n" -#~ " break;\n" -#~ " }\n" -#~ "\n" -#~ " let value = Store::::read_at_offset(address_domain, " -#~ "base, offset).unwrap();\n" -#~ " arr.append(value);\n" -#~ " offset += Store::::size();\n" -#~ " };\n" -#~ "\n" -#~ " // Return the array.\n" -#~ " Result::Ok(arr)\n" -#~ " }\n" -#~ "\n" -#~ " fn write_at_offset(\n" -#~ " address_domain: u32, base: StorageBaseAddress, mut offset: u8, " -#~ "mut value: Array\n" -#~ " ) -> SyscallResult<()> {\n" -#~ " // // Store the length of the array in the first storage slot.\n" -#~ " let len: u8 = value.len().try_into().expect('Storage - Span too " -#~ "large');\n" -#~ " Store::::write_at_offset(address_domain, base, offset, len);\n" -#~ " offset += 1;\n" -#~ "\n" -#~ " // Store the array elements sequentially\n" -#~ " loop {\n" -#~ " match value.pop_front() {\n" -#~ " Option::Some(element) => {\n" -#~ " Store::::write_at_offset(address_domain, " -#~ "base, offset, element);\n" -#~ " offset += Store::::size();\n" -#~ " },\n" -#~ " Option::None(_) => { break Result::Ok(()); }\n" -#~ " };\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " fn size() -> u8 {\n" -#~ " 255 * Store::::size()\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IStoreArrayContract {\n" -#~ " fn store_array(ref self: TContractState, arr: Array);\n" -#~ " fn read_array(self: @TContractState) -> Array;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod StoreArrayContract {\n" -#~ " use super::StoreFelt252Array;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " arr: Array\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl StoreArrayImpl of super::IStoreArrayContract {\n" -#~ " fn store_array(ref self: ContractState, arr: Array) {\n" -#~ " self.arr.write(arr);\n" -#~ " }\n" -#~ "\n" -#~ " fn read_array(self: @ContractState) -> Array {\n" -#~ " self.arr.read()\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IStoreArrayContract {\n" -#~ " fn store_array(ref self: TContractState, arr: Array);\n" -#~ " fn read_array(self: @TContractState) -> Array;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod StoreArrayContract {\n" -#~ " use super::StoreFelt252Array;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " arr: Array\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl StoreArrayImpl of super::IStoreArrayContract {\n" -#~ " fn store_array(ref self: ContractState, arr: Array) {\n" -#~ " self.arr.write(arr);\n" -#~ " }\n" -#~ "\n" -#~ " fn read_array(self: @ContractState) -> Array {\n" -#~ " self.arr.read()\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "#[derive(Copy, Drop, Serde, Hash)]\n" -#~ "struct Pet {\n" -#~ " name: felt252,\n" -#~ " age: u8,\n" -#~ " owner: felt252,\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IPetRegistry {\n" -#~ " fn register_pet(ref self: TContractState, key: Pet, timestamp: u64);\n" -#~ " fn get_registration_date(self: @TContractState, key: Pet) -> u64;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod PetRegistry {\n" -#~ " use hash::{HashStateTrait, Hash};\n" -#~ " use super::Pet;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " registration_time: LegacyMap::,\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl PetRegistry of super::IPetRegistry {\n" -#~ " fn register_pet(ref self: ContractState, key: Pet, timestamp: " -#~ "u64) {\n" -#~ " self.registration_time.write(key, timestamp);\n" -#~ " }\n" -#~ "\n" -#~ " fn get_registration_date(self: @ContractState, key: Pet) -> u64 " -#~ "{\n" -#~ " self.registration_time.read(key)\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[derive(Copy, Drop, Serde, Hash)]\n" -#~ "struct Pet {\n" -#~ " name: felt252,\n" -#~ " age: u8,\n" -#~ " owner: felt252,\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::interface]\n" -#~ "trait IPetRegistry {\n" -#~ " fn register_pet(ref self: TContractState, key: Pet, timestamp: u64);\n" -#~ " fn get_registration_date(self: @TContractState, key: Pet) -> u64;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod PetRegistry {\n" -#~ " use hash::{HashStateTrait, Hash};\n" -#~ " use super::Pet;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " registration_time: LegacyMap::,\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl PetRegistry of super::IPetRegistry {\n" -#~ " fn register_pet(ref self: ContractState, key: Pet, timestamp: " -#~ "u64) {\n" -#~ " self.registration_time.write(key, timestamp);\n" -#~ " }\n" -#~ "\n" -#~ " fn get_registration_date(self: @ContractState, key: Pet) -> u64 " -#~ "{\n" -#~ " self.registration_time.read(key)\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "# Hash Solidity Compatible" -#~ msgstr "# 兼容Hash Solidity" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait ISolidityHashExample {\n" -#~ " fn hash_data(ref self: TContractState, input_data: Span) -> " -#~ "u256;\n" -#~ "}\n" -#~ "\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod SolidityHashExample {\n" -#~ " use keccak::{keccak_u256s_be_inputs};\n" -#~ " use array::Span;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl SolidityHashExample of super::" -#~ "ISolidityHashExample {\n" -#~ " fn hash_data(ref self: ContractState, input_data: Span) -> " -#~ "u256 {\n" -#~ " let hashed = keccak_u256s_be_inputs(input_data);\n" -#~ "\n" -#~ " // Split the hashed value into two 128-bit segments\n" -#~ " let low: u128 = hashed.low;\n" -#~ " let high: u128 = hashed.high;\n" -#~ "\n" -#~ " // Reverse each 128-bit segment\n" -#~ " let reversed_low = integer::u128_byte_reverse(low);\n" -#~ " let reversed_high = integer::u128_byte_reverse(high);\n" -#~ "\n" -#~ " // Reverse merge the reversed segments back into a u256 " -#~ "value\n" -#~ " let compatible_hash = u256 { low: reversed_high, high: " -#~ "reversed_low };\n" -#~ "\n" -#~ " compatible_hash\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait ISolidityHashExample {\n" -#~ " fn hash_data(ref self: TContractState, input_data: Span) -> " -#~ "u256;\n" -#~ "}\n" -#~ "\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod SolidityHashExample {\n" -#~ " use keccak::{keccak_u256s_be_inputs};\n" -#~ " use array::Span;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {}\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl SolidityHashExample of super::" -#~ "ISolidityHashExample {\n" -#~ " fn hash_data(ref self: ContractState, input_data: Span) -> " -#~ "u256 {\n" -#~ " let hashed = keccak_u256s_be_inputs(input_data);\n" -#~ "\n" -#~ " // Split the hashed value into two 128-bit segments\n" -#~ " let low: u128 = hashed.low;\n" -#~ " let high: u128 = hashed.high;\n" -#~ "\n" -#~ " // Reverse each 128-bit segment\n" -#~ " let reversed_low = integer::u128_byte_reverse(low);\n" -#~ " let reversed_high = integer::u128_byte_reverse(high);\n" -#~ "\n" -#~ " // Reverse merge the reversed segments back into a u256 " -#~ "value\n" -#~ " let compatible_hash = u256 { low: reversed_high, high: " -#~ "reversed_low };\n" -#~ "\n" -#~ " compatible_hash\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "
Last change: 2023-11-21
" -#~ msgstr "
Last change: 2023-11-21
" - -#~ msgid "# Optimisations " -#~ msgstr "# 优化" - -#~ msgid "" -#~ "```rust\n" -#~ "trait StorePacking {\n" -#~ " fn pack(value: T) -> PackedT;\n" -#~ " fn unpack(value: PackedT) -> T;\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "trait StorePacking {\n" -#~ " fn pack(value: T) -> PackedT;\n" -#~ " fn unpack(value: PackedT) -> T;\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait ITime {\n" -#~ " fn set(ref self: TContractState, value: TimeContract::Time);\n" -#~ " fn get(self: @TContractState) -> TimeContract::Time;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod TimeContract {\n" -#~ " use starknet::storage_access::StorePacking;\n" -#~ " use integer::{\n" -#~ " U8IntoFelt252, Felt252TryIntoU16, U16DivRem, u16_as_non_zero, " -#~ "U16IntoFelt252,\n" -#~ " Felt252TryIntoU8\n" -#~ " };\n" -#~ " use traits::{Into, TryInto, DivRem};\n" -#~ " use option::OptionTrait;\n" -#~ " use serde::Serde;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " time: Time\n" -#~ " }\n" -#~ "\n" -#~ " #[derive(Copy, Serde, Drop)]\n" -#~ " struct Time {\n" -#~ " hour: u8,\n" -#~ " minute: u8\n" -#~ " }\n" -#~ "\n" -#~ " impl TimePackable of StorePacking {\n" -#~ " fn pack(value: Time) -> felt252 {\n" -#~ " let msb: felt252 = 256 * value.hour.into();\n" -#~ " let lsb: felt252 = value.minute.into();\n" -#~ " return msb + lsb;\n" -#~ " }\n" -#~ " fn unpack(value: felt252) -> Time {\n" -#~ " let value: u16 = value.try_into().unwrap();\n" -#~ " let (q, r) = U16DivRem::div_rem(value, " -#~ "u16_as_non_zero(256));\n" -#~ " let hour: u8 = Into::::into(q).try_into()." -#~ "unwrap();\n" -#~ " let minute: u8 = Into::::into(r).try_into()." -#~ "unwrap();\n" -#~ " return Time { hour, minute };\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl TimeContract of super::ITime {\n" -#~ " fn set(ref self: ContractState, value: Time) {\n" -#~ " // This will call the pack method of the TimePackable trait\n" -#~ " // and store the resulting felt252\n" -#~ " self.time.write(value);\n" -#~ " }\n" -#~ " fn get(self: @ContractState) -> Time {\n" -#~ " // This will read the felt252 value from storage\n" -#~ " // and return the result of the unpack method of the " -#~ "TimePackable trait\n" -#~ " return self.time.read();\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait ITime {\n" -#~ " fn set(ref self: TContractState, value: TimeContract::Time);\n" -#~ " fn get(self: @TContractState) -> TimeContract::Time;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod TimeContract {\n" -#~ " use starknet::storage_access::StorePacking;\n" -#~ " use integer::{\n" -#~ " U8IntoFelt252, Felt252TryIntoU16, U16DivRem, u16_as_non_zero, " -#~ "U16IntoFelt252,\n" -#~ " Felt252TryIntoU8\n" -#~ " };\n" -#~ " use traits::{Into, TryInto, DivRem};\n" -#~ " use option::OptionTrait;\n" -#~ " use serde::Serde;\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " time: Time\n" -#~ " }\n" -#~ "\n" -#~ " #[derive(Copy, Serde, Drop)]\n" -#~ " struct Time {\n" -#~ " hour: u8,\n" -#~ " minute: u8\n" -#~ " }\n" -#~ "\n" -#~ " impl TimePackable of StorePacking {\n" -#~ " fn pack(value: Time) -> felt252 {\n" -#~ " let msb: felt252 = 256 * value.hour.into();\n" -#~ " let lsb: felt252 = value.minute.into();\n" -#~ " return msb + lsb;\n" -#~ " }\n" -#~ " fn unpack(value: felt252) -> Time {\n" -#~ " let value: u16 = value.try_into().unwrap();\n" -#~ " let (q, r) = U16DivRem::div_rem(value, " -#~ "u16_as_non_zero(256));\n" -#~ " let hour: u8 = Into::::into(q).try_into()." -#~ "unwrap();\n" -#~ " let minute: u8 = Into::::into(r).try_into()." -#~ "unwrap();\n" -#~ " return Time { hour, minute };\n" -#~ " }\n" -#~ " }\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl TimeContract of super::ITime {\n" -#~ " fn set(ref self: ContractState, value: Time) {\n" -#~ " // This will call the pack method of the TimePackable trait\n" -#~ " // and store the resulting felt252\n" -#~ " self.time.write(value);\n" -#~ " }\n" -#~ " fn get(self: @ContractState) -> Time {\n" -#~ " // This will read the felt252 value from storage\n" -#~ " // and return the result of the unpack method of the " -#~ "TimePackable trait\n" -#~ " return self.time.read();\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "# List" -#~ msgstr "# 列表" - -#~ msgid "" -#~ "```rust\n" -#~ "#[storage]\n" -#~ "stuct Storage {\n" -#~ " amounts: List\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[storage]\n" -#~ "stuct Storage {\n" -#~ " amounts: List\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "trait ListTrait {\n" -#~ " fn len(self: @List) -> u32;\n" -#~ " fn is_empty(self: @List) -> bool;\n" -#~ " fn append(ref self: List, value: T) -> u32;\n" -#~ " fn get(self: @List, index: u32) -> Option;\n" -#~ " fn set(ref self: List, index: u32, value: T);\n" -#~ " fn pop_front(ref self: List) -> Option;\n" -#~ " fn array(self: @List) -> Array;\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "trait ListTrait {\n" -#~ " fn len(self: @List) -> u32;\n" -#~ " fn is_empty(self: @List) -> bool;\n" -#~ " fn append(ref self: List, value: T) -> u32;\n" -#~ " fn get(self: @List, index: u32) -> Option;\n" -#~ " fn set(ref self: List, index: u32, value: T);\n" -#~ " fn pop_front(ref self: List) -> Option;\n" -#~ " fn array(self: @List) -> Array;\n" -#~ "}\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "let second = self.amounts.read()[1];\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "let second = self.amounts.read()[1];\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "let mut amounts = self.amounts.read();\n" -#~ "amounts.append(42);\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "let mut amounts = self.amounts.read();\n" -#~ "amounts.append(42);\n" -#~ "```" - -#~ msgid "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IListExample {\n" -#~ " fn add_in_amount(ref self: TContractState, number: u128);\n" -#~ " fn add_in_task(ref self: TContractState, description: felt252, " -#~ "status: felt252);\n" -#~ " fn is_empty_list(self: @TContractState) -> bool;\n" -#~ " fn list_length(self: @TContractState) -> u32;\n" -#~ " fn get_from_index(self: @TContractState, index: u32) -> u128;\n" -#~ " fn set_from_index(ref self: TContractState, index: u32, number: " -#~ "u128);\n" -#~ " fn pop_front_list(ref self: TContractState);\n" -#~ " fn array_conversion(self: @TContractState) -> Array;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod ListExample {\n" -#~ " use alexandria_storage::list::{List, ListTrait};\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " amount: List,\n" -#~ " tasks: List\n" -#~ " }\n" -#~ "\n" -#~ " #[derive(Copy, Drop, Serde, starknet::Store)]\n" -#~ " struct Task {\n" -#~ " description: felt252,\n" -#~ " status: felt252\n" -#~ " }\n" -#~ "\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ListExample of super::IListExample {\n" -#~ " fn add_in_amount(ref self: ContractState, number: u128) {\n" -#~ " let mut current_amount_list = self.amount.read();\n" -#~ " current_amount_list.append(number);\n" -#~ " }\n" -#~ "\n" -#~ " fn add_in_task(ref self: ContractState, description: felt252, " -#~ "status: felt252) {\n" -#~ " let new_task = Task { description: description, status: " -#~ "status };\n" -#~ " let mut current_tasks_list = self.tasks.read();\n" -#~ " current_tasks_list.append(new_task);\n" -#~ " }\n" -#~ "\n" -#~ " fn is_empty_list(self: @ContractState) -> bool {\n" -#~ " let mut current_amount_list = self.amount.read();\n" -#~ " current_amount_list.is_empty()\n" -#~ " }\n" -#~ "\n" -#~ " fn list_length(self: @ContractState) -> u32 {\n" -#~ " let mut current_amount_list = self.amount.read();\n" -#~ " current_amount_list.len()\n" -#~ " }\n" -#~ "\n" -#~ " fn get_from_index(self: @ContractState, index: u32) -> u128 {\n" -#~ " self.amount.read()[index]\n" -#~ " }\n" -#~ "\n" -#~ " fn set_from_index(ref self: ContractState, index: u32, number: " -#~ "u128) {\n" -#~ " let mut current_amount_list = self.amount.read();\n" -#~ " current_amount_list.set(index, number);\n" -#~ " }\n" -#~ "\n" -#~ " fn pop_front_list(ref self: ContractState) {\n" -#~ " let mut current_amount_list = self.amount.read();\n" -#~ " current_amount_list.pop_front();\n" -#~ " }\n" -#~ "\n" -#~ " fn array_conversion(self: @ContractState) -> Array {\n" -#~ " let mut current_amount_list = self.amount.read();\n" -#~ " current_amount_list.array()\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" -#~ msgstr "" -#~ "```rust\n" -#~ "#[starknet::interface]\n" -#~ "trait IListExample {\n" -#~ " fn add_in_amount(ref self: TContractState, number: u128);\n" -#~ " fn add_in_task(ref self: TContractState, description: felt252, " -#~ "status: felt252);\n" -#~ " fn is_empty_list(self: @TContractState) -> bool;\n" -#~ " fn list_length(self: @TContractState) -> u32;\n" -#~ " fn get_from_index(self: @TContractState, index: u32) -> u128;\n" -#~ " fn set_from_index(ref self: TContractState, index: u32, number: " -#~ "u128);\n" -#~ " fn pop_front_list(ref self: TContractState);\n" -#~ " fn array_conversion(self: @TContractState) -> Array;\n" -#~ "}\n" -#~ "\n" -#~ "#[starknet::contract]\n" -#~ "mod ListExample {\n" -#~ " use alexandria_storage::list::{List, ListTrait};\n" -#~ "\n" -#~ " #[storage]\n" -#~ " struct Storage {\n" -#~ " amount: List,\n" -#~ " tasks: List\n" -#~ " }\n" -#~ "\n" -#~ " #[derive(Copy, Drop, Serde, starknet::Store)]\n" -#~ " struct Task {\n" -#~ " description: felt252,\n" -#~ " status: felt252\n" -#~ " }\n" -#~ "\n" -#~ "\n" -#~ " #[abi(embed_v0)]\n" -#~ " impl ListExample of super::IListExample {\n" -#~ " fn add_in_amount(ref self: ContractState, number: u128) {\n" -#~ " let mut current_amount_list = self.amount.read();\n" -#~ " current_amount_list.append(number);\n" -#~ " }\n" -#~ "\n" -#~ " fn add_in_task(ref self: ContractState, description: felt252, " -#~ "status: felt252) {\n" -#~ " let new_task = Task { description: description, status: " -#~ "status };\n" -#~ " let mut current_tasks_list = self.tasks.read();\n" -#~ " current_tasks_list.append(new_task);\n" -#~ " }\n" -#~ "\n" -#~ " fn is_empty_list(self: @ContractState) -> bool {\n" -#~ " let mut current_amount_list = self.amount.read();\n" -#~ " current_amount_list.is_empty()\n" -#~ " }\n" -#~ "\n" -#~ " fn list_length(self: @ContractState) -> u32 {\n" -#~ " let mut current_amount_list = self.amount.read();\n" -#~ " current_amount_list.len()\n" -#~ " }\n" -#~ "\n" -#~ " fn get_from_index(self: @ContractState, index: u32) -> u128 {\n" -#~ " self.amount.read()[index]\n" -#~ " }\n" -#~ "\n" -#~ " fn set_from_index(ref self: ContractState, index: u32, number: " -#~ "u128) {\n" -#~ " let mut current_amount_list = self.amount.read();\n" -#~ " current_amount_list.set(index, number);\n" -#~ " }\n" -#~ "\n" -#~ " fn pop_front_list(ref self: ContractState) {\n" -#~ " let mut current_amount_list = self.amount.read();\n" -#~ " current_amount_list.pop_front();\n" -#~ " }\n" -#~ "\n" -#~ " fn array_conversion(self: @ContractState) -> Array {\n" -#~ " let mut current_amount_list = self.amount.read();\n" -#~ " current_amount_list.array()\n" -#~ " }\n" -#~ " }\n" -#~ "}\n" -#~ "```" - -#~ msgid "
Last change: 2023-11-27
" -#~ msgstr "
Last change: 2023-11-27
" +#: src/ch02/list.md:65 +msgid "" +"```rust\n" +"#[starknet::interface]\n" +"trait IListExample {\n" +" fn add_in_amount(ref self: TContractState, number: u128);\n" +" fn add_in_task(ref self: TContractState, description: felt252, status: " +"felt252);\n" +" fn is_empty_list(self: @TContractState) -> bool;\n" +" fn list_length(self: @TContractState) -> u32;\n" +" fn get_from_index(self: @TContractState, index: u32) -> u128;\n" +" fn set_from_index(ref self: TContractState, index: u32, number: u128);\n" +" fn pop_front_list(ref self: TContractState);\n" +" fn array_conversion(self: @TContractState) -> Array;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod ListExample {\n" +" use alexandria_storage::list::{List, ListTrait};\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" amount: List,\n" +" tasks: List\n" +" }\n" +"\n" +" #[derive(Copy, Drop, Serde, starknet::Store)]\n" +" struct Task {\n" +" description: felt252,\n" +" status: felt252\n" +" }\n" +"\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ListExample of super::IListExample {\n" +" fn add_in_amount(ref self: ContractState, number: u128) {\n" +" let mut current_amount_list = self.amount.read();\n" +" current_amount_list.append(number);\n" +" }\n" +"\n" +" fn add_in_task(ref self: ContractState, description: felt252, status: " +"felt252) {\n" +" let new_task = Task { description: description, status: " +"status };\n" +" let mut current_tasks_list = self.tasks.read();\n" +" current_tasks_list.append(new_task);\n" +" }\n" +"\n" +" fn is_empty_list(self: @ContractState) -> bool {\n" +" let mut current_amount_list = self.amount.read();\n" +" current_amount_list.is_empty()\n" +" }\n" +"\n" +" fn list_length(self: @ContractState) -> u32 {\n" +" let mut current_amount_list = self.amount.read();\n" +" current_amount_list.len()\n" +" }\n" +"\n" +" fn get_from_index(self: @ContractState, index: u32) -> u128 {\n" +" self.amount.read()[index]\n" +" }\n" +"\n" +" fn set_from_index(ref self: ContractState, index: u32, number: u128) " +"{\n" +" let mut current_amount_list = self.amount.read();\n" +" current_amount_list.set(index, number);\n" +" }\n" +"\n" +" fn pop_front_list(ref self: ContractState) {\n" +" let mut current_amount_list = self.amount.read();\n" +" current_amount_list.pop_front();\n" +" }\n" +"\n" +" fn array_conversion(self: @ContractState) -> Array {\n" +" let mut current_amount_list = self.amount.read();\n" +" current_amount_list.array()\n" +" }\n" +" }\n" +"}\n" +"```" +msgstr "" +"```rust\n" +"#[starknet::interface]\n" +"trait IListExample {\n" +" fn add_in_amount(ref self: TContractState, number: u128);\n" +" fn add_in_task(ref self: TContractState, description: felt252, status: " +"felt252);\n" +" fn is_empty_list(self: @TContractState) -> bool;\n" +" fn list_length(self: @TContractState) -> u32;\n" +" fn get_from_index(self: @TContractState, index: u32) -> u128;\n" +" fn set_from_index(ref self: TContractState, index: u32, number: u128);\n" +" fn pop_front_list(ref self: TContractState);\n" +" fn array_conversion(self: @TContractState) -> Array;\n" +"}\n" +"\n" +"#[starknet::contract]\n" +"mod ListExample {\n" +" use alexandria_storage::list::{List, ListTrait};\n" +"\n" +" #[storage]\n" +" struct Storage {\n" +" amount: List,\n" +" tasks: List\n" +" }\n" +"\n" +" #[derive(Copy, Drop, Serde, starknet::Store)]\n" +" struct Task {\n" +" description: felt252,\n" +" status: felt252\n" +" }\n" +"\n" +"\n" +" #[abi(embed_v0)]\n" +" impl ListExample of super::IListExample {\n" +" fn add_in_amount(ref self: ContractState, number: u128) {\n" +" let mut current_amount_list = self.amount.read();\n" +" current_amount_list.append(number);\n" +" }\n" +"\n" +" fn add_in_task(ref self: ContractState, description: felt252, status: " +"felt252) {\n" +" let new_task = Task { description: description, status: " +"status };\n" +" let mut current_tasks_list = self.tasks.read();\n" +" current_tasks_list.append(new_task);\n" +" }\n" +"\n" +" fn is_empty_list(self: @ContractState) -> bool {\n" +" let mut current_amount_list = self.amount.read();\n" +" current_amount_list.is_empty()\n" +" }\n" +"\n" +" fn list_length(self: @ContractState) -> u32 {\n" +" let mut current_amount_list = self.amount.read();\n" +" current_amount_list.len()\n" +" }\n" +"\n" +" fn get_from_index(self: @ContractState, index: u32) -> u128 {\n" +" self.amount.read()[index]\n" +" }\n" +"\n" +" fn set_from_index(ref self: ContractState, index: u32, number: u128) " +"{\n" +" let mut current_amount_list = self.amount.read();\n" +" current_amount_list.set(index, number);\n" +" }\n" +"\n" +" fn pop_front_list(ref self: ContractState) {\n" +" let mut current_amount_list = self.amount.read();\n" +" current_amount_list.pop_front();\n" +" }\n" +"\n" +" fn array_conversion(self: @ContractState) -> Array {\n" +" let mut current_amount_list = self.amount.read();\n" +" current_amount_list.array()\n" +" }\n" +" }\n" +"}\n" +"```" + +#: src/ch02/list.md:139 +msgid "
Last change: 2023-11-27
" +msgstr "
Last change: 2023-11-27
" From a3a79b5c88d8f15be626c4490ff858e20db29459 Mon Sep 17 00:00:00 2001 From: cryptonerdcn Date: Thu, 7 Dec 2023 00:13:21 +0900 Subject: [PATCH 2/2] Add Spanish to language selections. --- theme/index.hbs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/theme/index.hbs b/theme/index.hbs index 8c2e178c..23d75322 100644 --- a/theme/index.hbs +++ b/theme/index.hbs @@ -163,6 +163,9 @@
  • +