diff --git "a/01.Cadence \347\274\226\347\250\213\350\257\255\350\250\200" "b/01.Cadence \347\274\226\347\250\213\350\257\255\350\250\200.md" similarity index 100% rename from "01.Cadence \347\274\226\347\250\213\350\257\255\350\250\200" rename to "01.Cadence \347\274\226\347\250\213\350\257\255\350\250\200.md" diff --git "a/21\345\220\210\345\220\214.md" "b/21\345\220\210\347\272\246.md" similarity index 55% rename from "21\345\220\210\345\220\214.md" rename to "21\345\220\210\347\272\246.md" index b35dd10..16cc4f7 100644 --- "a/21\345\220\210\345\220\214.md" +++ "b/21\345\220\210\347\272\246.md" @@ -1,389 +1,389 @@ -# 合同 - ------- - -Cadence 中的 合同是接口、结构、资源、数据(其状态)和代码(其功能)的类型定义的集合,这些类型定义位于 Flow 中帐户的 合同存储区域中。 - - 合同是必须在 Cadence 中定义这些类型的所有复合类型(如结构、资源、事件和接口)的地方。因此,如果未在部署的 Cadence 合同中定义,则不能存在这些类型的对象。 - -可以使用[授权帐户](https://docs.onflow.org/cadence/language/accounts)`contracts` 对象创建、更新和删除合同。此功能将在[下一节中介绍](https://docs.onflow.org/cadence/language/contracts/#deploying,-updating,-and-removing-contracts) - -合同是类型。它们类似于复合类型,但与结构或资源的存储方式不同,并且不能像资源或结构一样用作值、 复制或移动。 - - 合同保留在账户的合同存储区中,只能由账户所有者使用特殊命令添加、更新或删除。 - -合同是使用`contract`关键字声明的。关键字后跟合同名称。 - -```cadence -pub contract SomeContract { - // ... -} -``` - -合同不能相互嵌套。 - -```cadence -pub contract Invalid { - - // 无效: 合同不能嵌套在任何其他类型中。 - // - pub contract Nested { - // ... - } -} -``` - -最简单的 合同形式之一就是具有状态字段、函数和`init`初始化字段的函数: - - - -```cadence -pub contract HelloWorld { - - // 在 HelloWorld 中声明一个存储状态字段 - // - pub let greeting: String - - // Declare a function that can be called by anyone - // who imports the contract - //声明一个想导入 合同的任何人都可以调用的函数 - pub fun hello(): String { - return self.greeting - } - - init() { - self.greeting = "Hello World!" - } -} -``` - -该合同可以部署到一个帐户并永久存在于 合同存储中。交易和其他 合同可以通过在交易或 合同定义开始时导入它们来与 合同交互。 - -任何人都可以`hello`通过从部署到的账户中导入合同并使用导入的对象调用 hello 函数来调用上述 合同的函数。 - - - -```cadence -import HelloWorld from 0x42 - -// 无效: 合同不知道hello来自哪里 -// -log(hello()) // 错误 - -// 有效:使用导入的 合同对象调用hello功能 -// -log(HelloWorld.hello()) // 打印 "Hello World!" - -// Valid: 使用导入的 合同对象读取问候语场地。 -log(HelloWorld.greeting) // 打印 "Hello World!" - -//无效: 合同创建后无法调用init函数。 -// -HelloWorld.init() // Error -``` - -每个账户可以有任意数量的 合同,它们可以包含任意数量的数据。这意味着 合同可以有任意数量的字段、函数和类型定义,但它们必须在 合同中,而不是另一个顶级定义。 - -```cadence -// 无效:顶级声明仅限于 合同或 合同接口。因此,所有这些都将无效 -// 如果它们被部署到账户 合同存储因此部署将被拒绝。 -// -pub resource Vault {} -pub struct Hat {} -pub fun helloWorld(): String {} -let num: Int -``` - - 合同的另一个重要特征是 合同中声明的资源和事件的实例只能在同一 合同中声明的函数或类型中创建/发出。 - -不可能在 合同之外创建资源和事件的实例。 - -下面的 合同定义了一个资源接口`Receiver`和一个`Vault` 实现该接口的资源。这个例子的编写方式,没有办法创建这个资源,所以它不可用。 - -```cadence -// 无效 -pub contract FungibleToken { - - pub resource interface Receiver { - - pub balance: Int - - pub fun deposit(from: @{Receiver}) { - pre { - from.balance > 0: - "Deposit balance needs to be positive!" - } - post { - self.balance == before(self.balance) + before(from.balance): - "Incorrect amount removed" - } - } - } - - pub resource Vault: Receiver { - - //跟踪账户代币的总余额 - pub var balance: Int - - init(balance: Int) { - self.balance = balance - } - - // 取款从金库余额中减去金额,然后返回一个减去余额的金库对象 - pub fun withdraw(amount: Int): @Vault { - self.balance = self.balance - amount - return <-create Vault(balance: amount) - } - - // 存款将保险库对象作为参数并添加它的余额到账户金库的余额,然后销毁发送的保管库,因为其余额已被消耗 - pub fun deposit(from: @{Receiver}) { - self.balance = self.balance + from.balance - destroy from - } - } -} -``` - -如果用户尝试运行创建该`Vault`类型实例的事务,则类型检查器将不允许这样做,因为只有`FungibleToken` 合同中的代码才能创建新的`Vault`s。 - - - -```cadence -import FungibleToken from 0x42 - -// 无效:无法在外部创建 `Vault` 类型的实例定义`Vault`的 合同 -// -let newVault <- create FungibleToken.Vault(balance: 10) -``` - - 合同必须定义一个创建新`Vault`实例的函数, 或者使用它的`init`函数创建一个实例并将其存储在所有者的帐户存储中。 - -这带来了 Cadence 合同的另一个关键特征。 合同可以与其帐户`storage`和`published`对象交互以存储资源、结构和引用。他们通过使用`self.account`只能在合同中访问的特殊对象来做到这一点。 - -想象一下,这些是在上面的`FungibleToken` 合同中声明的。 - - - -```cadence - pub fun createVault(initialBalance: Int): @Vault { - return <-create Vault(balance: initialBalance) - } - - init(balance: Int) { - let vault <- create Vault(balance: 1000) - self.account.save(<-vault, to: /storage/initialVault) - } -``` - -现在,任何账户都可以调用`createVault` 合同中声明的函数来创建`Vault`对象。或者所有者可以`withdraw`自己调用该函数`Vault`将新的保险库发送给其他人。 - - - -```cadence -import FungibleToken from 0x42 - -// 有效:通过调用 合同的创建一个 `Vault` 类型的实例`createVault` 函数。 -// -let newVault <- create FungibleToken.createVault(initialBalance: 10) -``` - - 合同具有隐式字段`let account: Account`,它也是部署 合同的帐户。这使 合同能够读取和写入帐户的存储。 - -## 部署、更新和删除合同 - -为了在 Cadence 中使用 合同,需要将其部署到帐户中。可以通过`contracts`对象访问帐户的已部署 合同。 - -### 已部署的 合同 - -账户存储“部署的 合同”,即 合同的代码: - - - -```cadence -struct DeployedContract { - let name: String - let code: [UInt8] -} -``` - -注意,这不是可以通过导入获取的 合同实例。 - -### 部署新 合同 - -可以使用以下`add`功能将新 合同部署到帐户: - - - -```cadence -fun add( - name: String, - code: [UInt8], - ... contractInitializerArguments -): DeployedContract -``` - -将给定的合同添加到帐户中。 - -该`code`参数是源代码的 UTF-8 编码表示。代码必须只包含一个 合同或 合同接口,并且必须与`name`参数同名。 - -给定的所有附加参数都进一步传递给正在部署的 合同的初始值设定项。 - -如果账户中已经存在具有给定名称的 合同/ 合同接口,如果给定的代码没有准确声明一个 合同或 合同接口,或者给定的名称与 合同/ 合同接口声明的名称不匹配,则失败代码。 - -返回[部署的合同](https://docs.onflow.org/cadence/language/contracts/#deployed-contracts)。 - -例如,假设应部署以下 合同代码: - - - -```cadence -pub contract Test { - pub let message: String - - init(message: String) { - self.message = message - } -} -``` - - 合同可以部署如下: - - - -```cadence -// 使用内置函数`decodeHex`将十六进制编码的源代码解码为字节数组。(省略号...表示字符串的剩余部分) -// -let code = "70756220636f6e...".decodeHex() - -// `code` 的类型为 `[UInt8]` - -let signer: Account = ... -signer.contracts.add( - name: "Test", - code: code, - message: "I'm a new contract in an existing account" -) -``` - -### 更新已部署的合同 - -> 🚧 状态:更新合同是**实验性的**。 -> -> 更新 合同目前仅限于保持数据一致性。在[合同可更新性](https://docs.onflow.org/cadence/language/contract-updatability)部分阅读有关更新合同的有效更改和限制的更多详细信息 。 - -可以使用以下`update__experimental`函数更新已部署的 合同: - - - -```cadence -fun update__experimental(name: String, code: [UInt8]): DeployedContract -``` - -更新账户中 合同/ 合同接口的代码。 - -该`code`参数是源代码的 UTF-8 编码表示。代码必须只包含一个合同或合同接口,并且必须与`name`参数同名。 - -无需再次运行该合同/合同接口的初始化。全局状态中合同实例保持原样。 - -如果账户中不存在具有给定名称的 合同/ 合同接口,如果给定的代码没有准确声明一个合同或合同接口,或者给定的名称与代码中的合同/ 合同接口声明的名称不匹配,则失败返回更新合同的[部署](https://docs.onflow.org/cadence/language/contracts/#deployed-contracts)合同。 - -例如,假设一个名为的合同`Test`已经部署到账户中,并且应该使用以下 合同代码进行更新: - -```cadence -pub contract Test { - pub let message: String - - init(message: String) { - self.message = message - } -} -``` - - 合同可以更新如下: - -```cadence -// 使用内置函数`decodeHex`将十六进制编码的源代码解码为字节数组。(省略号...表示字符串的剩余部分) -// -let code = "70756220636f6e...".decodeHex() - -// `code` 的类型为 `[UInt8]` - -let signer: Account = ... -signer.contracts.update__experimental(name: "Test", code: code) -``` - -### 获取已部署的合同 - -可以使用以下`get`函数从帐户中获取已部署的合同: - -```cadence -fun get(name: String): DeployedContract? -``` - -返回在帐户中具有给定名称的合同/合同接口的已[部署合同](https://docs.onflow.org/cadence/language/contracts/#deployed-contracts)(如果有)。 - -`nil`如果帐户中不存在具有给定名称的 合同/ 合同接口,则 返回。 - -例如,假设一个名为的 合同`Test`部署到一个账户,可以按如下方式检索该合同: - -```cadence -let signer: Account = ... -let contract = signer.contracts.get(name: "Test") -``` - -### 删除已部署的 合同 - -可以使用以下`remove`函数从帐户中删除已部署的 合同: - - - -```cadence -fun remove(name: String): DeployedContract? -``` - -从具有给定名称的帐户中删除合同/合同接口(如果有)。 - -返回已删除的已[部署合同](https://docs.onflow.org/cadence/language/contracts/#deployed-contracts)(如果有)。 - -如果帐户中不存在具有给定名称的 合同/ 合同接口,则 返回`nil`。 - -例如,假设一个名为的 合同`Test`部署到一个账户,可以移除该合同: - -```cadence -let signer: Account = ... -let contract = signer.contracts.remove(name: "Test") -``` - -## 合同接口 - -像复合类型一样, 合同可以有接口来指定关于它们的行为、它们的类型和它们的类型的行为的规则。 - -合同接口必须全局声明。声明不能嵌套在其他类型中。 - -如果协定接口声明了具体类型,则它的实现也必须声明符合类型要求的相同具体类型。 - -如果 合同接口声明了接口类型,则实现 合同不必也定义该接口。他们可以通过说来引用该嵌套接口`{ContractInterfaceName}.{NestedInterfaceName}` - -```cadence -// 声明一个 合同接口,声明一个接口和一个资源并需要在合同实现中实现该接口。 -// -pub contract interface InterfaceExample { - - // 实现不需要声明 this,应将其称为 InterfaceExample.NestedInterface - // - pub resource interface NestedInterface {} - - // 实现必须声明这种类型 - // - pub resource Composite: NestedInterface {} -} - -pub contract ExampleContract: InterfaceExample { - - // 合同不需要重新声明`NestedInterface`接口,因为它已经在 合同接口中声明了 - // 资源必须使用名称来引用资源接口访问它的 合同接口 - // - pub resource Composite: InterfaceExample.NestedInterface { - } -} +# 合约 + +------ + +Cadence 中的 合约是接口、结构、资源、数据(其状态)和代码(其功能)的类型定义的集合,这些类型定义位于 Flow 中帐户的 合约存储区域中。 + + 合约是必须在 Cadence 中定义这些类型的所有复合类型(如结构、资源、事件和接口)的地方。因此,如果未在部署的 Cadence 合约中定义,则不能存在这些类型的对象。 + +可以使用[授权帐户](https://docs.onflow.org/cadence/language/accounts)`contracts` 对象创建、更新和删除合约。此功能将在[下一节中介绍](https://docs.onflow.org/cadence/language/contracts/#deploying,-updating,-and-removing-contracts) + +合约是类型。它们类似于复合类型,但与结构或资源的存储方式不同,并且不能像资源或结构一样用作值、 复制或移动。 + + 合约保留在账户的合约存储区中,只能由账户所有者使用特殊命令添加、更新或删除。 + +合约是使用`contract`关键字声明的。关键字后跟合约名称。 + +```cadence +pub contract SomeContract { + // ... +} +``` + +合约不能相互嵌套。 + +```cadence +pub contract Invalid { + + // 无效: 合约不能嵌套在任何其他类型中。 + // + pub contract Nested { + // ... + } +} +``` + +最简单的 合约形式之一就是具有状态字段、函数和`init`初始化字段的函数: + + + +```cadence +pub contract HelloWorld { + + // 在 HelloWorld 中声明一个存储状态字段 + // + pub let greeting: String + + // Declare a function that can be called by anyone + // who imports the contract + //声明一个想导入 合约的任何人都可以调用的函数 + pub fun hello(): String { + return self.greeting + } + + init() { + self.greeting = "Hello World!" + } +} +``` + +该合约可以部署到一个帐户并永久存在于 合约存储中。交易和其他 合约可以通过在交易或 合约定义开始时导入它们来与 合约交互。 + +任何人都可以`hello`通过从部署到的账户中导入合约并使用导入的对象调用 hello 函数来调用上述 合约的函数。 + + + +```cadence +import HelloWorld from 0x42 + +// 无效: 合约不知道hello来自哪里 +// +log(hello()) // 错误 + +// 有效:使用导入的 合约对象调用hello功能 +// +log(HelloWorld.hello()) // 打印 "Hello World!" + +// Valid: 使用导入的 合约对象读取问候语场地。 +log(HelloWorld.greeting) // 打印 "Hello World!" + +//无效: 合约创建后无法调用init函数。 +// +HelloWorld.init() // Error +``` + +每个账户可以有任意数量的 合约,它们可以包含任意数量的数据。这意味着 合约可以有任意数量的字段、函数和类型定义,但它们必须在 合约中,而不是另一个顶级定义。 + +```cadence +// 无效:顶级声明仅限于 合约或 合约接口。因此,所有这些都将无效 +// 如果它们被部署到账户 合约存储因此部署将被拒绝。 +// +pub resource Vault {} +pub struct Hat {} +pub fun helloWorld(): String {} +let num: Int +``` + + 合约的另一个重要特征是 合约中声明的资源和事件的实例只能在同一 合约中声明的函数或类型中创建/发出。 + +不可能在 合约之外创建资源和事件的实例。 + +下面的 合约定义了一个资源接口`Receiver`和一个`Vault` 实现该接口的资源。这个例子的编写方式,没有办法创建这个资源,所以它不可用。 + +```cadence +// 无效 +pub contract FungibleToken { + + pub resource interface Receiver { + + pub balance: Int + + pub fun deposit(from: @{Receiver}) { + pre { + from.balance > 0: + "Deposit balance needs to be positive!" + } + post { + self.balance == before(self.balance) + before(from.balance): + "Incorrect amount removed" + } + } + } + + pub resource Vault: Receiver { + + //跟踪账户代币的总余额 + pub var balance: Int + + init(balance: Int) { + self.balance = balance + } + + // 取款从金库余额中减去金额,然后返回一个减去余额的金库对象 + pub fun withdraw(amount: Int): @Vault { + self.balance = self.balance - amount + return <-create Vault(balance: amount) + } + + // 存款将保险库对象作为参数并添加它的余额到账户金库的余额,然后销毁发送的保管库,因为其余额已被消耗 + pub fun deposit(from: @{Receiver}) { + self.balance = self.balance + from.balance + destroy from + } + } +} +``` + +如果用户尝试运行创建该`Vault`类型实例的事务,则类型检查器将不允许这样做,因为只有`FungibleToken` 合约中的代码才能创建新的`Vault`s。 + + + +```cadence +import FungibleToken from 0x42 + +// 无效:无法在外部创建 `Vault` 类型的实例定义`Vault`的 合约 +// +let newVault <- create FungibleToken.Vault(balance: 10) +``` + + 合约必须定义一个创建新`Vault`实例的函数, 或者使用它的`init`函数创建一个实例并将其存储在所有者的帐户存储中。 + +这带来了 Cadence 合约的另一个关键特征。 合约可以与其帐户`storage`和`published`对象交互以存储资源、结构和引用。他们通过使用`self.account`只能在合约中访问的特殊对象来做到这一点。 + +想象一下,这些是在上面的`FungibleToken` 合约中声明的。 + + + +```cadence + pub fun createVault(initialBalance: Int): @Vault { + return <-create Vault(balance: initialBalance) + } + + init(balance: Int) { + let vault <- create Vault(balance: 1000) + self.account.save(<-vault, to: /storage/initialVault) + } +``` + +现在,任何账户都可以调用`createVault` 合约中声明的函数来创建`Vault`对象。或者所有者可以`withdraw`自己调用该函数`Vault`将新的保险库发送给其他人。 + + + +```cadence +import FungibleToken from 0x42 + +// 有效:通过调用 合约的创建一个 `Vault` 类型的实例`createVault` 函数。 +// +let newVault <- create FungibleToken.createVault(initialBalance: 10) +``` + + 合约具有隐式字段`let account: Account`,它也是部署 合约的帐户。这使 合约能够读取和写入帐户的存储。 + +## 部署、更新和删除合约 + +为了在 Cadence 中使用 合约,需要将其部署到帐户中。可以通过`contracts`对象访问帐户的已部署 合约。 + +### 已部署的 合约 + +账户存储“部署的 合约”,即 合约的代码: + + + +```cadence +struct DeployedContract { + let name: String + let code: [UInt8] +} +``` + +注意,这不是可以通过导入获取的 合约实例。 + +### 部署新 合约 + +可以使用以下`add`功能将新 合约部署到帐户: + + + +```cadence +fun add( + name: String, + code: [UInt8], + ... contractInitializerArguments +): DeployedContract +``` + +将给定的合约添加到帐户中。 + +该`code`参数是源代码的 UTF-8 编码表示。代码必须只包含一个 合约或 合约接口,并且必须与`name`参数同名。 + +给定的所有附加参数都进一步传递给正在部署的 合约的初始值设定项。 + +如果账户中已经存在具有给定名称的 合约/ 合约接口,如果给定的代码没有准确声明一个 合约或 合约接口,或者给定的名称与 合约/ 合约接口声明的名称不匹配,则失败代码。 + +返回[部署的合约](https://docs.onflow.org/cadence/language/contracts/#deployed-contracts)。 + +例如,假设应部署以下 合约代码: + + + +```cadence +pub contract Test { + pub let message: String + + init(message: String) { + self.message = message + } +} +``` + + 合约可以部署如下: + + + +```cadence +// 使用内置函数`decodeHex`将十六进制编码的源代码解码为字节数组。(省略号...表示字符串的剩余部分) +// +let code = "70756220636f6e...".decodeHex() + +// `code` 的类型为 `[UInt8]` + +let signer: Account = ... +signer.contracts.add( + name: "Test", + code: code, + message: "I'm a new contract in an existing account" +) +``` + +### 更新已部署的合约 + +> 🚧 状态:更新合约是**实验性的**。 +> +> 更新 合约目前仅限于保持数据一致性。在[合约可更新性](https://docs.onflow.org/cadence/language/contract-updatability)部分阅读有关更新合约的有效更改和限制的更多详细信息 。 + +可以使用以下`update__experimental`函数更新已部署的 合约: + + + +```cadence +fun update__experimental(name: String, code: [UInt8]): DeployedContract +``` + +更新账户中 合约/ 合约接口的代码。 + +该`code`参数是源代码的 UTF-8 编码表示。代码必须只包含一个合约或合约接口,并且必须与`name`参数同名。 + +无需再次运行该合约/合约接口的初始化。全局状态中合约实例保持原样。 + +如果账户中不存在具有给定名称的 合约/ 合约接口,如果给定的代码没有准确声明一个合约或合约接口,或者给定的名称与代码中的合约/ 合约接口声明的名称不匹配,则失败返回更新合约的[部署](https://docs.onflow.org/cadence/language/contracts/#deployed-contracts)合约。 + +例如,假设一个名为的合约`Test`已经部署到账户中,并且应该使用以下 合约代码进行更新: + +```cadence +pub contract Test { + pub let message: String + + init(message: String) { + self.message = message + } +} +``` + + 合约可以更新如下: + +```cadence +// 使用内置函数`decodeHex`将十六进制编码的源代码解码为字节数组。(省略号...表示字符串的剩余部分) +// +let code = "70756220636f6e...".decodeHex() + +// `code` 的类型为 `[UInt8]` + +let signer: Account = ... +signer.contracts.update__experimental(name: "Test", code: code) +``` + +### 获取已部署的合约 + +可以使用以下`get`函数从帐户中获取已部署的合约: + +```cadence +fun get(name: String): DeployedContract? +``` + +返回在帐户中具有给定名称的合约/合约接口的已[部署合约](https://docs.onflow.org/cadence/language/contracts/#deployed-contracts)(如果有)。 + +`nil`如果帐户中不存在具有给定名称的 合约/ 合约接口,则 返回。 + +例如,假设一个名为的 合约`Test`部署到一个账户,可以按如下方式检索该合约: + +```cadence +let signer: Account = ... +let contract = signer.contracts.get(name: "Test") +``` + +### 删除已部署的 合约 + +可以使用以下`remove`函数从帐户中删除已部署的 合约: + + + +```cadence +fun remove(name: String): DeployedContract? +``` + +从具有给定名称的帐户中删除合约/合约接口(如果有)。 + +返回已删除的已[部署合约](https://docs.onflow.org/cadence/language/contracts/#deployed-contracts)(如果有)。 + +如果帐户中不存在具有给定名称的 合约/ 合约接口,则 返回`nil`。 + +例如,假设一个名为的 合约`Test`部署到一个账户,可以移除该合约: + +```cadence +let signer: Account = ... +let contract = signer.contracts.remove(name: "Test") +``` + +## 合约接口 + +像复合类型一样, 合约可以有接口来指定关于它们的行为、它们的类型和它们的类型的行为的规则。 + +合约接口必须全局声明。声明不能嵌套在其他类型中。 + +如果协定接口声明了具体类型,则它的实现也必须声明符合类型要求的相同具体类型。 + +如果 合约接口声明了接口类型,则实现 合约不必也定义该接口。他们可以通过说来引用该嵌套接口`{ContractInterfaceName}.{NestedInterfaceName}` + +```cadence +// 声明一个 合约接口,声明一个接口和一个资源并需要在合约实现中实现该接口。 +// +pub contract interface InterfaceExample { + + // 实现不需要声明 this,应将其称为 InterfaceExample.NestedInterface + // + pub resource interface NestedInterface {} + + // 实现必须声明这种类型 + // + pub resource Composite: NestedInterface {} +} + +pub contract ExampleContract: InterfaceExample { + + // 合约不需要重新声明`NestedInterface`接口,因为它已经在 合约接口中声明了 + // 资源必须使用名称来引用资源接口访问它的 合约接口 + // + pub resource Composite: InterfaceExample.NestedInterface { + } +} ``` \ No newline at end of file diff --git "a/22\345\220\210\345\220\214\346\233\264\346\226\260.md" "b/22\345\220\210\347\272\246\346\233\264\346\226\260.md" similarity index 79% rename from "22\345\220\210\345\220\214\346\233\264\346\226\260.md" rename to "22\345\220\210\347\272\246\346\233\264\346\226\260.md" index 1f9a41f..bd8ed3e 100644 --- "a/22\345\220\210\345\220\214\346\233\264\346\226\260.md" +++ "b/22\345\220\210\347\272\246\346\233\264\346\226\260.md" @@ -1,414 +1,414 @@ -# 合同可更新性 - ------- - -## 介绍 - -Cadence 中的 合同是数据(其状态)和代码(其功能)的集合,它们位于账户的合同存储区中。更新合同时,重要的是要确保引入的更改不会导致已存储数据的运行时不一致。 Cadence 通过在更新之前验证合同及其所有组件来保持这种状态一致性。 - -## 验证目标 - -合同更新验证确保: - -- 更新合同时,存储的数据不会改下变其含义。 -- 解码和使用存储的数据不会导致运行时崩溃。 - - 例如,添加字段是无效的,因为现有存储的数据不会有新字段。 - - 加载现有数据将导致此类字段的垃圾值/缺失值。 - - 对该字段访问的静态检查是有效的,但在访问该字段时解释器会崩溃,因为该字段具有缺失/垃圾值。 - -但是,它**不能**确保: - -- 任何导入更新 合同的程序都保持有效。例如: - - 更新的 合同可能会删除现有字段或可能会更改函数签名。 - - 然后任何使用该字段/函数的程序都会出现语义错误。 - -## 更新 合同 - -可以通过添加新合同、删除现有合同或更新现有合同来更改合同。但是,其中一些更改可能会导致上述数据不一致。 - -#### 有效变更 - -- 添加新合同有效。 -- 删除没有枚举声明的合同/合同接口是有效的。 -- 在满足以下部分所述的限制,更新合同是有效的。 - -#### 无效更改 - -- 删除包含枚举声明的合同/合同接口是无效的。 - - 删除合同允许添加具有相同名称的新合同。 - - 新合同可能具有与旧合同中名称相同但结构不同的枚举声明。 - - 这可能会改变那些枚举类型已经存储的值的含义。 - - 合同可能包含字段和其他声明,例如复合类型、函数、构造函数等。当更新现有合同时,也会验证其所有内部声明。 - -### 合同字段 - -部署合同时,合同的字段存储在帐户的合同存储中。更改合同的字段只会更改程序处理数据的方式,但不会更改已存储的数据本身,这可能会导致上一节中提到的运行时不一致。 - -请参阅[下面有关字段](https://docs.onflow.org/cadence/language/contract-updatability/#fields)的[部分,了解](https://docs.onflow.org/cadence/language/contract-updatability/#fields)可以[对字段](https://docs.onflow.org/cadence/language/contract-updatability/#fields)进行的可能更新,以及对更改合同字段施加的限制。 - -### 嵌套声明 - -合同可以具有嵌套的复合类型声明,例如结构、资源、接口和枚举。更新合同时,会检查其嵌套声明,因为: - -- 它们可以直接或间接用作同一 合同字段的类型注释。 -- 任何第三方合同都可以导入本 合同中定义的类型并将其用作类型注释。 -- 因此,更改类型定义与更改此类字段的类型注释相同(这也是无效的,如下面[关于字段字段](https://docs.onflow.org/cadence/language/contract-updatability/#fields)的[部分](https://docs.onflow.org/cadence/language/contract-updatability/#fields)所述)。 - -以下部分描述了可以对嵌套声明进行的更改和更新限制: - -- [结构、资源和接口](https://docs.onflow.org/cadence/language/contract-updatability/#structs-resources-and-interfaces) -- [枚举](https://docs.onflow.org/cadence/language/contract-updatability/#enums) -- [函数](https://docs.onflow.org/cadence/language/contract-updatability/#functions) -- [构造函数](https://docs.onflow.org/cadence/language/contract-updatability/#constructors) - -## 字段 - -字段可能属于合同、结构、资源或接口。 - -#### 有效更改: - -- 删除字段有效 - - ```cadence - // 现有合同 - - pub contract Foo { - pub var a: String - pub var b: Int - } - - - // 更新合同 - - pub contract Foo { - pub var a: String - } - ``` - - - 它使已删除字段的数据在存储中未使用,因为它不再可访问。 - - 但是,它不会导致任何运行时崩溃。 - -- 更改字段顺序是有效的。 - - ```cadence - // 现有合同 - - pub contract Foo { - pub var a: String - pub var b: Int - } - - - //更新合同 - - pub contract Foo { - pub var b: Int - pub var a: String - } - ``` - -- 更改字段的访问修饰符是有效的。 - - ```cadence - // 现有合同 - - pub contract Foo { - pub var a: String - } - - - // 更新合同 - - pub contract Foo { - priv var a: Int //权限更改为私有 - } - ``` - -#### 无效更改 - -- 添加新字段无效。 - - ```cadence - // 现有合同 - - pub contract Foo { - pub var a: String - } - - - // 现有合同 - - pub contract Foo { - pub var a: String - pub var b: Int // 无效的新字段 - } - ``` - - - 合同的初始化程序仅在第一次部署 合同时运行一次。当 合同更新时它不会重新运行。 - - 因此,存储的数据不会有新字段,因为不会执行新添加字段的初始化。 - - 解码存储的数据将导致这些字段的垃圾或缺失值。 - -- 更改现有字段的类型无效。 - - - - ```cadence - // 现有合同 - - pub contract Foo { - pub var a: String - } - - - // 现有合同 - - pub contract Foo { - pub var a: Int // 无效的更改类型 - } - ``` - - - 在已存储的合同中,该字段`a`的值为 type `String`。 - - 将字段的类型更改`a`为`Int`, 将使运行时将已存储的`String` 值读取为`Int`,这将导致反序列化错误。 - - 将字段类型更改为现有类型的子类型/超类型也是无效的,因为它也可能在解码/编码时导致问题。 - - 例如:将`Int64`字段更改为`Int8`- 存储字段可能有一个数字值`624`,这超出了 的值空间`Int8`。 - - 但是,这是当前实现中的一个限制,Cadence 的未来版本可能支持将字段类型更改为子类型,通过提供迁移现有字段的方法。 - -## 结构、资源和接口 - -#### 有效更改: - -- 添加新的结构、资源或接口是有效的。 - -- 添加、删除或更改结构/资源的接口一致性是有效的,因为存储的数据仅存储具体类型/值,但不存储一致性信息。 - - - - ```cadence - // 现有结构 - - pub struct Foo: T { - } - - - // 更新结构 - - pub struct Foo: R { - } - ``` - -#### 无效更改: - -- 删除现有声明无效。 - - - 删除声明允许添加具有相同名称但具有不同结构的新声明。 - - 任何使用该声明的程序都将面临存储数据的不一致问题。 - -- 重命名声明无效。它可以具有与删除现有声明并添加新声明相同的效果。 - -- 更改声明类型无效。即:从结构更改为接口,反之亦然。 - - - - ```cadence - // 现有结构 - - pub struct Foo { - } - - - // 更改为接口结构 - - pub struct interface Foo { // 无效的类型转换 - } - ``` - -### 更新成员 - -与合同类似,这些复合声明:结构、资源和接口也可以将字段和其他嵌套声明作为其成员。更新这样的复合声明还包括更新其所有成员。 - -以下部分描述了对更新结构、资源或接口的成员施加的限制。 - -- [字段](https://docs.onflow.org/cadence/language/contract-updatability/#fields) -- [嵌套结构、资源和接口](https://docs.onflow.org/cadence/language/contract-updatability/#structs-resources-and-interfaces) -- [枚举](https://docs.onflow.org/cadence/language/contract-updatability/#enums) -- [函数](https://docs.onflow.org/cadence/language/contract-updatability/#functions) -- [构造函数](https://docs.onflow.org/cadence/language/contract-updatability/#constructors) - -## 枚举 - -#### 有效更改: - -- 添加新的枚举声明是有效的。 - -#### 无效更改: - -- 删除现有的枚举声明无效。 - - - 否则,可以删除现有枚举并添加具有相同名称但具有不同结构的新枚举声明。 - - 新结构可能具有不兼容的更改(例如更改的类型、更改的枚举案例等)。 - -- 更改名称无效,因为它等同于删除现有枚举并添加新枚举。 - -- 更改原始类型无效。 - - - - ```cadence - // 现有原始Int枚举 - - pub enum Color: Int { - pub case RED - pub case BLUE - } - - - // 更新原始UInt8枚举 - - pub enum Color: UInt8 { // 无效改变类型 - pub case RED - pub case BLUE - } - ``` - - - 存储枚举值时,将存储与枚举大小写关联的原始值。 - - 如果类型发生更改,并且已存储的值与更新的类型不在同一值空间中,则反序列化可能会失败。 - -### 更新枚举案例 - -枚举由枚举案例声明组成,更新枚举也可能包括更改枚举案例。枚举案例在 Cadence 解释器和运行时使用它们的原始值表示。因此,不允许任何导致 enum-case 更改其原始值的更改。否则,更改的原始值可能会导致已存储的枚举值与原来的含义不同(类型混淆)。 - -#### 有效更改: - -- 在现有 enum-case 的末尾添加 enum-case 是有效的。 - - - - ```cadence - // 现有枚举 - - pub enum Color: Int { - pub case RED - pub case BLUE - } - - - // 更新枚举 - - pub enum Color: Int { - pub case RED - pub case BLUE - pub case GREEN // 无效的新增枚举成员 - } - ``` - - #### 无效更改 - -- 在现有 enum-case 的顶部或中间添加 enum-case 是无效的。 - - - - ```cadence - // 现有枚举 - - pub enum Color: Int { - pub case RED - pub case BLUE - } - - - // 更新枚举 - - pub enum Color: Int { - pub case RED - pub case GREEN // 无效的新增枚举成员 - pub case BLUE - } - ``` - -- 更改枚举大小写的名称无效。 - - ```cadence - // 现有枚举 - - pub enum Color: Int { - pub case RED - pub case BLUE - } - - - // 更新枚举 - - pub enum Color: Int { - pub case RED - pub case GREEN // 无效的更改名称 - } - ``` - - - 以前存储的原始值`Color.BLUE`现在代表`Color.GREEN`. 即:存储的值已更改其含义,因此不是有效的更改。 - - 类似地,可以添加一个具有旧 name 的新枚举`BLUE`,它会获得一个新的原始值。那么相同的 enum-case`Color.BLUE`可能在运行时使用了两个 raw-values,在更改之前和之后,这也是无效的。 - -- 删除枚举大小写无效。删除允许添加和删除与重命名具有相同效果的枚举案例。 - - - - ```cadence - // 现有枚举 - - pub enum Color: Int { - pub case RED - pub case BLUE - } - - - // 更新枚举 - - pub enum Color: Int { - pub case RED - - //无效的删除成员 - } - ``` - -- 不允许更改 enum-case 的顺序 - - - - ```cadence - // 现有枚举 - - pub enum Color: Int { - pub case RED - pub case BLUE - } - - - // 更新枚举 - - pub enum Color: UInt8 { - pub case BLUE // 无效的顺序更改 - pub case RED - } - ``` - - - 枚举的原始值是隐式的,对应于定义的顺序。 - - 更改 enum-case 的顺序与更改 raw-value 具有相同的效果,这可能会导致存储不一致和类型混淆,如前所述。 - -## 函数 - -更新函数定义始终有效,因为函数定义永远不会存储为数据。即:函数定义是代码的一部分,而不是数据。 - -- 更改函数签名(参数、返回类型)是有效的。 -- 更改函数体也是有效的。 -- 更改访问修饰符是有效的。 - -但是,更改*函数类型*可能有效也可能无效,具体取决于使用它的位置。即:如果在复合类型字段(直接或间接)的类型注释中使用了函数类型,则更改函数类型签名与更改该字段的类型注释相同(再次无效)。 - -## 构造函数 - -与函数类似,构造函数也不存储。因此,对构造函数的任何更改都是有效的。 - -## 导入 - - 合同可以从其他程序导入声明(类型、函数、变量等)。这些导入的程序在部署时已经过验证。因此,每次导入时无需验证任何声明。 - +# 合约可更新性 + +------ + +## 介绍 + +Cadence 中的 合约是数据(其状态)和代码(其功能)的集合,它们位于账户的合约存储区中。更新合约时,重要的是要确保引入的更改不会导致已存储数据的运行时不一致。 Cadence 通过在更新之前验证合约及其所有组件来保持这种状态一致性。 + +## 验证目标 + +合约更新验证确保: + +- 更新合约时,存储的数据不会改下变其含义。 +- 解码和使用存储的数据不会导致运行时崩溃。 + - 例如,添加字段是无效的,因为现有存储的数据不会有新字段。 + - 加载现有数据将导致此类字段的垃圾值/缺失值。 + - 对该字段访问的静态检查是有效的,但在访问该字段时解释器会崩溃,因为该字段具有缺失/垃圾值。 + +但是,它**不能**确保: + +- 任何导入更新 合约的程序都保持有效。例如: + - 更新的 合约可能会删除现有字段或可能会更改函数签名。 + - 然后任何使用该字段/函数的程序都会出现语义错误。 + +## 更新 合约 + +可以通过添加新合约、删除现有合约或更新现有合约来更改合约。但是,其中一些更改可能会导致上述数据不一致。 + +#### 有效变更 + +- 添加新合约有效。 +- 删除没有枚举声明的合约/合约接口是有效的。 +- 在满足以下部分所述的限制,更新合约是有效的。 + +#### 无效更改 + +- 删除包含枚举声明的合约/合约接口是无效的。 + - 删除合约允许添加具有相同名称的新合约。 + - 新合约可能具有与旧合约中名称相同但结构不同的枚举声明。 + - 这可能会改变那些枚举类型已经存储的值的含义。 + + 合约可能包含字段和其他声明,例如复合类型、函数、构造函数等。当更新现有合约时,也会验证其所有内部声明。 + +### 合约字段 + +部署合约时,合约的字段存储在帐户的合约存储中。更改合约的字段只会更改程序处理数据的方式,但不会更改已存储的数据本身,这可能会导致上一节中提到的运行时不一致。 + +请参阅[下面有关字段](https://docs.onflow.org/cadence/language/contract-updatability/#fields)的[部分,了解](https://docs.onflow.org/cadence/language/contract-updatability/#fields)可以[对字段](https://docs.onflow.org/cadence/language/contract-updatability/#fields)进行的可能更新,以及对更改合约字段施加的限制。 + +### 嵌套声明 + +合约可以具有嵌套的复合类型声明,例如结构、资源、接口和枚举。更新合约时,会检查其嵌套声明,因为: + +- 它们可以直接或间接用作同一 合约字段的类型注释。 +- 任何第三方合约都可以导入本 合约中定义的类型并将其用作类型注释。 +- 因此,更改类型定义与更改此类字段的类型注释相同(这也是无效的,如下面[关于字段字段](https://docs.onflow.org/cadence/language/contract-updatability/#fields)的[部分](https://docs.onflow.org/cadence/language/contract-updatability/#fields)所述)。 + +以下部分描述了可以对嵌套声明进行的更改和更新限制: + +- [结构、资源和接口](https://docs.onflow.org/cadence/language/contract-updatability/#structs-resources-and-interfaces) +- [枚举](https://docs.onflow.org/cadence/language/contract-updatability/#enums) +- [函数](https://docs.onflow.org/cadence/language/contract-updatability/#functions) +- [构造函数](https://docs.onflow.org/cadence/language/contract-updatability/#constructors) + +## 字段 + +字段可能属于合约、结构、资源或接口。 + +#### 有效更改: + +- 删除字段有效 + + ```cadence + // 现有合约 + + pub contract Foo { + pub var a: String + pub var b: Int + } + + + // 更新合约 + + pub contract Foo { + pub var a: String + } + ``` + + - 它使已删除字段的数据在存储中未使用,因为它不再可访问。 + - 但是,它不会导致任何运行时崩溃。 + +- 更改字段顺序是有效的。 + + ```cadence + // 现有合约 + + pub contract Foo { + pub var a: String + pub var b: Int + } + + + //更新合约 + + pub contract Foo { + pub var b: Int + pub var a: String + } + ``` + +- 更改字段的访问修饰符是有效的。 + + ```cadence + // 现有合约 + + pub contract Foo { + pub var a: String + } + + + // 更新合约 + + pub contract Foo { + priv var a: Int //权限更改为私有 + } + ``` + +#### 无效更改 + +- 添加新字段无效。 + + ```cadence + // 现有合约 + + pub contract Foo { + pub var a: String + } + + + // 现有合约 + + pub contract Foo { + pub var a: String + pub var b: Int // 无效的新字段 + } + ``` + + - 合约的初始化程序仅在第一次部署 合约时运行一次。当 合约更新时它不会重新运行。 + - 因此,存储的数据不会有新字段,因为不会执行新添加字段的初始化。 + - 解码存储的数据将导致这些字段的垃圾或缺失值。 + +- 更改现有字段的类型无效。 + + + + ```cadence + // 现有合约 + + pub contract Foo { + pub var a: String + } + + + // 现有合约 + + pub contract Foo { + pub var a: Int // 无效的更改类型 + } + ``` + + - 在已存储的合约中,该字段`a`的值为 type `String`。 + - 将字段的类型更改`a`为`Int`, 将使运行时将已存储的`String` 值读取为`Int`,这将导致反序列化错误。 + - 将字段类型更改为现有类型的子类型/超类型也是无效的,因为它也可能在解码/编码时导致问题。 + - 例如:将`Int64`字段更改为`Int8`- 存储字段可能有一个数字值`624`,这超出了 的值空间`Int8`。 + - 但是,这是当前实现中的一个限制,Cadence 的未来版本可能支持将字段类型更改为子类型,通过提供迁移现有字段的方法。 + +## 结构、资源和接口 + +#### 有效更改: + +- 添加新的结构、资源或接口是有效的。 + +- 添加、删除或更改结构/资源的接口一致性是有效的,因为存储的数据仅存储具体类型/值,但不存储一致性信息。 + + + + ```cadence + // 现有结构 + + pub struct Foo: T { + } + + + // 更新结构 + + pub struct Foo: R { + } + ``` + +#### 无效更改: + +- 删除现有声明无效。 + + - 删除声明允许添加具有相同名称但具有不同结构的新声明。 + - 任何使用该声明的程序都将面临存储数据的不一致问题。 + +- 重命名声明无效。它可以具有与删除现有声明并添加新声明相同的效果。 + +- 更改声明类型无效。即:从结构更改为接口,反之亦然。 + + + + ```cadence + // 现有结构 + + pub struct Foo { + } + + + // 更改为接口结构 + + pub struct interface Foo { // 无效的类型转换 + } + ``` + +### 更新成员 + +与合约类似,这些复合声明:结构、资源和接口也可以将字段和其他嵌套声明作为其成员。更新这样的复合声明还包括更新其所有成员。 + +以下部分描述了对更新结构、资源或接口的成员施加的限制。 + +- [字段](https://docs.onflow.org/cadence/language/contract-updatability/#fields) +- [嵌套结构、资源和接口](https://docs.onflow.org/cadence/language/contract-updatability/#structs-resources-and-interfaces) +- [枚举](https://docs.onflow.org/cadence/language/contract-updatability/#enums) +- [函数](https://docs.onflow.org/cadence/language/contract-updatability/#functions) +- [构造函数](https://docs.onflow.org/cadence/language/contract-updatability/#constructors) + +## 枚举 + +#### 有效更改: + +- 添加新的枚举声明是有效的。 + +#### 无效更改: + +- 删除现有的枚举声明无效。 + + - 否则,可以删除现有枚举并添加具有相同名称但具有不同结构的新枚举声明。 + - 新结构可能具有不兼容的更改(例如更改的类型、更改的枚举案例等)。 + +- 更改名称无效,因为它等同于删除现有枚举并添加新枚举。 + +- 更改原始类型无效。 + + + + ```cadence + // 现有原始Int枚举 + + pub enum Color: Int { + pub case RED + pub case BLUE + } + + + // 更新原始UInt8枚举 + + pub enum Color: UInt8 { // 无效改变类型 + pub case RED + pub case BLUE + } + ``` + + - 存储枚举值时,将存储与枚举大小写关联的原始值。 + - 如果类型发生更改,并且已存储的值与更新的类型不在同一值空间中,则反序列化可能会失败。 + +### 更新枚举案例 + +枚举由枚举案例声明组成,更新枚举也可能包括更改枚举案例。枚举案例在 Cadence 解释器和运行时使用它们的原始值表示。因此,不允许任何导致 enum-case 更改其原始值的更改。否则,更改的原始值可能会导致已存储的枚举值与原来的含义不同(类型混淆)。 + +#### 有效更改: + +- 在现有 enum-case 的末尾添加 enum-case 是有效的。 + + + + ```cadence + // 现有枚举 + + pub enum Color: Int { + pub case RED + pub case BLUE + } + + + // 更新枚举 + + pub enum Color: Int { + pub case RED + pub case BLUE + pub case GREEN // 无效的新增枚举成员 + } + ``` + + #### 无效更改 + +- 在现有 enum-case 的顶部或中间添加 enum-case 是无效的。 + + + + ```cadence + // 现有枚举 + + pub enum Color: Int { + pub case RED + pub case BLUE + } + + + // 更新枚举 + + pub enum Color: Int { + pub case RED + pub case GREEN // 无效的新增枚举成员 + pub case BLUE + } + ``` + +- 更改枚举大小写的名称无效。 + + ```cadence + // 现有枚举 + + pub enum Color: Int { + pub case RED + pub case BLUE + } + + + // 更新枚举 + + pub enum Color: Int { + pub case RED + pub case GREEN // 无效的更改名称 + } + ``` + + - 以前存储的原始值`Color.BLUE`现在代表`Color.GREEN`. 即:存储的值已更改其含义,因此不是有效的更改。 + - 类似地,可以添加一个具有旧 name 的新枚举`BLUE`,它会获得一个新的原始值。那么相同的 enum-case`Color.BLUE`可能在运行时使用了两个 raw-values,在更改之前和之后,这也是无效的。 + +- 删除枚举大小写无效。删除允许添加和删除与重命名具有相同效果的枚举案例。 + + + + ```cadence + // 现有枚举 + + pub enum Color: Int { + pub case RED + pub case BLUE + } + + + // 更新枚举 + + pub enum Color: Int { + pub case RED + + //无效的删除成员 + } + ``` + +- 不允许更改 enum-case 的顺序 + + + + ```cadence + // 现有枚举 + + pub enum Color: Int { + pub case RED + pub case BLUE + } + + + // 更新枚举 + + pub enum Color: UInt8 { + pub case BLUE // 无效的顺序更改 + pub case RED + } + ``` + + - 枚举的原始值是隐式的,对应于定义的顺序。 + - 更改 enum-case 的顺序与更改 raw-value 具有相同的效果,这可能会导致存储不一致和类型混淆,如前所述。 + +## 函数 + +更新函数定义始终有效,因为函数定义永远不会存储为数据。即:函数定义是代码的一部分,而不是数据。 + +- 更改函数签名(参数、返回类型)是有效的。 +- 更改函数体也是有效的。 +- 更改访问修饰符是有效的。 + +但是,更改*函数类型*可能有效也可能无效,具体取决于使用它的位置。即:如果在复合类型字段(直接或间接)的类型注释中使用了函数类型,则更改函数类型签名与更改该字段的类型注释相同(再次无效)。 + +## 构造函数 + +与函数类似,构造函数也不存储。因此,对构造函数的任何更改都是有效的。 + +## 导入 + + 合约可以从其他程序导入声明(类型、函数、变量等)。这些导入的程序在部署时已经过验证。因此,每次导入时无需验证任何声明。 +