Cadence 还是 Solidity:链上代币转移的深入研究
福洛链 Flow Official
2023-02-14 07:00
订阅此专栏
收藏此文章

原文标题:

Cadence or Solidity: On-Chain Token Transfer Deep Dive

原文链接:https://flow.com/engineering-blogs/flow-blockchain-programming-language-smart-contract-cadence-solidity-comparison-ethereum

翻译:Lanford,Flow 大使。

ERC-20 和 ERC-721 可能是迄今为止最成功、最广泛应用的智能合约标准,对以太坊的成功发挥了关键作用。然而,Flow 上的 Cadence 智能合约语言却采取了不同的做法。


在本文的第一部分,我们将比较 ERC-20/ERC-721 与它们在 Flow 上的对应物,以说明 Solidity 和 Cadence 代币转移方法的根本差异。我们将探讨每种智能合约语言如何影响代币转移,以及 Cadence 的设计如何提供新的语言和安全功能,从而使建设者和用户受益。我们假设读者对使用 Solidity 或其他智能合约语言进行去中心化应用程序(dApp)开发有基本的了解。



参考合约

作为 Solidity 的参考,我们使用 OpenZeppelin 提供的优秀且常用的 ERC-20 和 ERC-721 实现。

ERC-20:https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol

ERC-721:https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol


■ 高层次的比较

Cadence 的设计,以及其同质化代币(FT)和非同质化代币(NFT)标准受到了 ERC-20 和 ERC-721 的实际使用经验的启发。(实际上,Cadence 及其代币标准的主要设计者之一是 ERC-721 的原作者)。 对两种标准在核心功能上进行比较是合理的,因为它们都为各方的代币交换提供了便利。但是,除了共同的目标外,这种比较在接口和代码层面上是不可行的。然而,两种语言在功能和设计上的差异对建设者有重要影响,这些影响涉及到应用程序的设计、安全性和可组合性。


■ 代币转移机制的比较

Solidity 合约的一个决定性特征是,每个代币合约存储和管理着代币余额或 NFT 的所有者以及对这些数据的访问权限。Solidity 依赖基于地址的访问和授权模型,在执行任何重要功能之前,会通过检查你的地址来验证你是谁。而在 Cadence 中,所有使用 FT / NFT 标准的代币都是被称为资源的一等公民类型,其唯一性由编程语言原生地进行管理。访问和安全是通过基于能力(Capability)的访问控制来管理的,这些程序化的对象类似于加密钥匙,当持有者拥有这些钥匙时,就能获得访问另一个对象的某些范围的权利。


Flow 账户模型与资源和能力一起提供了新的原语和独特的功能,吸收了复杂性,而不是将其留给开发者,使 dApp 的开发比以往更容易。


■ ERC-20 代币委托转移 transferFrom()

代币委托转移模式在基于 EVM 的 dApp 中已经无处不在,是说明基于地址的访问模式所带来的一些挑战的理想例子。该图以 GIBBON 代币的 ERC-20 合约为例,显示了当有人从 dApp/ 服务合约中执行委托代币转移方法 transferFrom() 时的典型程序流程。

approve() 这一步骤,对于 Metamask 用户来说都是非常熟悉的,大多数 dApp 都要求执行它,它也几乎是运行所有基于 EVM 链的功能的前提。在用户与 dApp 合约进行后续互动之前,用户在 GIBBON 合约上授权 dApp 合约动用一定的金额。然后,dApp 合约调用引用发送方的 transferFrom(),从而将 10 个 GIB 代币从发送方转移到自身。


上述流程完成后,GIBBON 代币合约中存储的 _balances 映射将在用户账户对应的代币余额中扣除给定的金额(不得超过用户授权中指定的金额),同时也为 dApp 合约地址在 _balances 映射中对应的代币余额增加相同的金额。从根本上说,这是因为合约作为一个集中的分类账,记录所有者与他们的余额的映射,类似于银行账户。

mapping(address => unit256) private _balances;


■ 为什么要委托?

退一步讲,GIBBON 合约需要 approve() 步骤的原因是,合约是代币,也存储所有者与余额的映射数据。给予 dApp 合约的授权类似于某人得到一张签名的银行汇票,允许他们为自己提取发送者的价值。因此,授权已经成为 Solidity 安全结构的一个关键部分,这是有原因的。然而,再往前走一步,授权模式必须存在的原因是 Solidity 以地址为中心的性质。与任何 Solidity 合约的交互都假定与该合约交互的账户是执行该函数的账户。运行时上下文通过 msg.sender 属性提供了调用合约的地址,并且在函数调用期间,只有这个地址被授权。

function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {    address spender = _msgSender();    _spendAllowance(from, spender, amount);    _transfer(from, to, amount);    return true;}

因此,由于 dApp 合约与 GIBBON 合约的互动将反映其在 msg.sender 中的地址,代表发送方行事的唯一方法是首先得到授权。知道了这一点,我们可以看到 transferFrom() 方法在应用任何变化之前在方法内部进行了这种检查,在 OpenZeppelin 基础合约中,这种检查在 spendAllowance()allowance() 函数中进行。

function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {    uint256 currentAllowance = allowance(owner, spender);    if (currentAllowance != type(uint256).max) {        require(currentAllowance >= amount, "ERC20: insufficient allowance");        unchecked {            _approve(owner, spender, currentAllowance - amount);        }    }}
function allowance(address owner, address spender) public view virtual override returns (uint256) {    return _allowances[owner][spender];}



Cadence 是不同的,但不同在哪里?

正如前面所谈到的,Cadence 引入了新的概念,这使其解决代币转移的方式非常不同。要了解 Cadence 中代币转移的机制,就必须知道以下内容:


■ 账户模型

 Cadence 的账户模型将与账户相关的密钥和代码("智能合约")的存储,以及该账户拥有的资产的存储相结合。这是正确的:在 Cadence 中,你的代币被存储在你的账户中,而不是智能合约中。当然,智能合约仍然定义这些资产和它们的行为方式,但这些资产可以通过资源的魔力安全地存储在用户的账户中。


■ 资源

资源是唯一的、线性的类型,不能被复制或隐含地丢弃,只能在账户之间移动。如果在开发过程中,一个函数未能在函数范围内存储一个从账户中获得的资源,语义检查将会标记出错误。在允许的操作方面,运行时也执行同样严格的规则。因此,在退出前没有正确处理作用域中的资源的合约函数将被中止,存储将被恢复到原始状态。资源的这些特性使它们非常适合用来表示代币,包括同质化的和非同质化的。所有权是通过它们的存储位置来追踪的,而且由于语言本身会强制执行正确性,所以资产不会被复制或意外丢失。


■ 能力

对存储对象的远程访问可以通过”能力“进行委托。这意味着,如果一个账户想要访问另一个账户的存储对象,它必须被赋予一个该对象的有效“能力”(Capability)。能力可以是公共的,也可以是私有的。如果一个账户想让所有其他账户访问,它可以共享一个公共能力。( 例如,账户通过一个公共能力接受来自所有来源的同质化代币存款是很常见的)。另外,账户可以将私有能力授予特定的账户,以提供对有限功能的访问。例如,NFT 项目通常通过 "管理员能力 "控制铸币,授予特定账户铸币的权力。


■ 合约标准

Cadence FT 合约标准定义了与 ERC-20 类似的待实现合约接口。该标准也延伸到子类型,如资源、资源接口或其他类型。因此,该标准能够定义和限制行为以及 / 或者设定 FT 实现不能违反的条件。具体而言,FT 标准定义了金库资源子类型,意味着该标准的所有实施必须实现相同的金库资源类型,并具有相同的接口和功能。提供者、接受者和余额的资源接口将金库的相关操作分成几部分,用于细粒度的安全控制,我们将在后面展开讨论。在 FT 标准金库函数中指定的函数前置及后置条件在运行时会强制执行检查。该标准忽略了铸币、创建和其他关切,因为这些可能因项目而异。下面的类图显示了 FT 合约标准以及 FLOW 代币是如何实现它的。


■ 从 msg.sender 中解脱

为了理解为什么这些新的概念很重要,我们将快速了解一些历史。Cadence 作为一种语言的开发始于 2018 年,由 Dapper Labs 团队开发,他们长期以来一直在与 Solidity 的限制作斗争。dApp 工程中最令人沮丧的方面可以追溯到基于地址的访问,这使得合约非常难以组合。Web3 中的可组合性是指一个合约作为一块乐高积木,其他合约可以围绕这块积木进行建造,将它们的功能加在一起。例如,如果一个游戏合约在链上存储了游戏的输赢结果,就可以创建另一个合约,使用输赢数据来显示排行榜上的顶级玩家。另一个合约可能更进一步,使用赢 / 输的历史来计算赔率,对玩家的未来游戏进行投注。然而,由于基于地址的访问,合约的调用被牢牢限定在某人是谁上。这使得如果第一个合约不能访问第二个合约,即使用户对两个合约都有访问权,两个合约也不可能进行互动。


Solidity 中的访问控制的方向性是从受保护的功能到被授权的主体。因此,合约内部化授权,并检查所有访问受保护功能的地址。

Cadence 用“能力”扭转了访问范式。一旦获得能力,就被授予了受保护函数(或资源)的访问权,所以合约不再需要授权地址。这是因为只有通过能力使用 borrow() 才能访问受保护的对象。再见 msg.sender!

这对可组合性的影响是很大的。如果合约不需要预先知道交互是代表谁进行的,那么用户就有可能在交易中与任意数量的合约及其功能进行交互,只要他们拥有能力。这也意味着合约之间可以直接互动,无需审批或其他设置,唯一的要求是调用方拥有所需的能力。要想更详细、更深入地了解 Flow 可组合性的广阔和令人兴奋的可能性,请关注目前正在撰写的一篇文章。



从授权委托思维到领域驱动的编码

与其说代码直接与 FT 代币合约互动,不如说第一步是与账户互动,为特定的 FT 代币类型获取能力。在图中,我们看到能力是针对 FLOW 金库的,正如其公共路径参数所确定的(是的,你可以列出账户所拥有的公共能力)。返回的能力实例使调用者能够 borrow() 引用的金库资源,在本例中是 FLOW 金库对象。在我们的例子中,返回类型被限制为 FT 接收器资源接口。这就严格限制了对金库引用的调用,调用方只能调用 deposit() 函数。


正如我们将在下面探讨的那样,这个代码是直观的,面向对象的,而且类型明显与代币转移领域有关。



交易的基本要素

让我们使用你对基于能力的访问和 FT 标准的新理解,遍历代币转移交易所需的基本要素。


■ 1. 获得提供者的金库以提取资金

正如上一节所概述的,获得对包含资金的用户金库的访问权是必要的第一步。在这一步中,用户是资金被发送并为执行交易的一方,因此他们可以使用 borrow() 直接访问自己的提供者金库,而无需首先获得 Capability。

// The Vault Resource that holds the tokens that are being transferredlet interimVault: @FungibleToken.Vault
prepare(provider: AuthAccount) {
 // Get a reference to the provider's FLOW vault  let vaultRef = provider.borrow<&FlowToken.Vault>(from: FlowToken.VaultStoragePath)    ?? panic("Could not borrow reference to the provider's Vault!")
 // Withdraw tokens from the provider's vault  self.interimVault <- vaultRef.withdraw(amount: amount)}

interimVault 常量被定义在交易代码的顶部,用于在存入收款人之前暂时保存从提供者那里提取的代币。也许很微妙,使用移动 ← 操作符将提供者的金库代币移动到 interimVault,验证要提取的金额是否可用。这些步骤都发生在交易的准备块中,该块用于从提供者的存储中加载对象。如果提供者没有足够的代币,或者如果金库在某种程度上不可用,交易将中止。


■ 2. 获得一个作为存放目标的接收方金库的引用

Cadence 交易发生在两个阶段,其中的执行阶段是为了处理之前获得的提供者资源,并与其他账户进行互动。当准备工作成功完成后,将会进入到执行阶段。请注意,在这个阶段中,接收方金库资源引用必须通过接收方账户的公共能力获得,否则会没有访问权。

execute {
 // Get the recipient's public account object  let recipient = getAccount(to)
 // Get a reference to the recipient's FLOW vault receiver  let receiverRef = recipient.getCapability(FlowToken.ReceiverPublicPath)    .borrow<&{FungibleToken.Receiver}>()    ?? panic("Could not borrow receiver reference to the recipient's Vault")
   // Deposit the withdrawn tokens in the recipient's receiver    receiverRef.deposit(from: <-self.interimVault)}

我们从账户中获得与 FlowToken.ReceiverPublicPath 相匹配的公共能力,然后通过该能力借用 FLOW 金库的接收器引用。我们通过使用移动操作符 ← 将 interimVault 中的金额存入接收者的金库来结束交易。执行完成后,该交易会被提交到链上。如果在任何阶段发生错误,整个交易将被中止。


■ 做个总结

正如上面的代码片段所提示的,Cadence 中的交易与 Solidity 不同,它们可以包含任何任意数量的代码,而不仅仅是一个函数调用。在下面的完整例子中,FT 标准和 FLOW 代币的合约被导入并解析到交易范围内,相关类型被引入,之后与这些类型的交互将遵循标准的面向对象编程惯例。

import FungibleToken from 0xFUNGIBLETOKENADDRESSimport FlowToken from 0xTOKENADDRESS
transaction(amount: UFix64, to: Address) {
   // The Vault Resource that holds the tokens that are being transferred    let interimVault: @FungibleToken.Vault
   prepare(provider: AuthAccount) {
       // Get a reference to the provider's FLOW vault        let vaultRef = provider.borrow<&FlowToken.Vault>(from: FlowToken.VaultStoragePath)      ?? panic("Could not borrow reference to the provider's Vault!")
       // Withdraw tokens from the provider's vault        self.interimVault <- vaultRef.withdraw(amount: amount)    }
   execute {
       // Get the recipient's public account object        let recipient = getAccount(to)
       // Get a reference to the recipient's FLOW vault receiver        let receiverRef = recipient.getCapability(FlowToken.ReceiverPublicPath)            .borrow<&{FungibleToken.Receiver}>()      ?? panic("Could not borrow receiver reference to the recipient's Vault")
       // Deposit the withdrawn tokens in the recipient's receiver        receiverRef.deposit(from: <-self.interimVault)    }}


■ 这怎么会是安全的,难道不是任何人都能从金库内获取全部资金吗?

乍一看,授予账户金库的访问权似乎很不安全,令人担忧。然而,账户初始化设置时会确保缩小公共能力的访问权限的范围,以防止用户访问全部金库接口。这个例子显示了在初始化设置期间将代币金库的能力链接到账户中时,FLOW 代币合约是如何缩小访问范围的:

// Create a public capability to the Vault that only exposes// the deposit function through the Receiver interfacesigner.link<&FlowToken.Vault{FungibleToken.Receiver}>(    /public/flowTokenReceiver,    target: /storage/flowTokenVault)

链接的能力返回对账户金库的弱化版引用,只暴露了 FlowToken.Vault{FungibleToken.Receiver} 接口。这使得尽管源对象具有更广泛的 API 接口,访问者也不能在金库上调用其他函数。使用弱化来缩小暴露的能力的访问范围是 Cadence 独特的、普遍被使用的安全控制机制之一。



Cadence 中关于授权的真相

敏锐的人可能已经注意到上面的转移例子并没有委托控制。事实是,由于基于地址和基于的安全模型是不同的,所以在 Solidity 和 Cadence 之间不可能对委托转移进行同类比较。也就是说,授权是可能的——只是不是以 Solidity 的方式进行。


■ 通过能力进行授权

委托访问或控制的最简单方式是通过能力。如果 Alice 决定把代币转移委托给 Bob,允许他把她的一些资金花在一些东西上,她将向 Bob 发出一个私有的 Capability,专门授予对她的代币库上的提供者资源接口的访问权。从 Alice 的主 FLOW 金库中提取无限资金的权限显然是不可取的。有了 Capabilities,就有可能设计出最适合自己用例的富有表现力和创造性的解决方案,这里的例子只是人们可能用来实现委托的一些方法。

  1. 在 Alice 的账户中创建一个新的、临时的 Flow 金库,其资金来源于 Alice 允许 Bob 提取的有限资金。然后将一个私有能力链接到该金库,并将其提供给 Bob。

  2. 创建一个新的资源—— 在这里以 ScopedProvider 为例(完整的例子 Gist:https://gist.github.com/sisyphusSmiling/418ca0bd9a734eb6750f40449c849140)—— 实现提供者接口。在该资源中,我们在 Alice 的金库上保存一个提供者 Capability,并通过包装资源的 withdraw() 方法强制执行一个提款限制。然后,ScopedProvider 可以被保存在 Alice 的账户中,它的提供者 Capability 被链接并交给 Bob

/// Resource wrapping a Provider Capability to enforce a withdrawal limitpub resource ScopedProvider : FungibleToken.Provider {  /// Track how much has been withdrawn  pub var withdrawn: UFix64  /// Set a limit on withdrawal  pub let withdrawalLimit: UFix64  /// Maintain a Capability to the source Provider  pub let sourceProvider: Capability<&{FungibleToken.Provider}>
 init(    _ withdrawalLimit: UFix64,    _ sourceProvider: Capability<&{FungibleToken.Provider}>  ) {    pre {      sourceProvider.check(): "Problem with given Provider Capability!"    }    /* Assign resource variables on init */    self.withdrawn = 0.0    self.withdrawalLimit = withdrawalLimit    self.sourceProvider = sourceProvider  }    /// Provider.withdraw method implementation that enforces withdrawal limit  pub fun withdraw(amount: UFix64): @FungibleToken.Vault {    pre {      self.withdrawn + amount <= self.withdrawalLimit: "Amount exceeds remaining withdrawal limit!"    }    // Increment withdrawn amount before withdrawing    self.withdrawn = self.withdrawn + amount    // Get a reference to the Provider given on init    let sourceProviderRef = self.sourceProvider.borrow()!    // Return a Vault withdrawn from the source provider    return <-sourceProviderRef.withdraw(amount: amount)  }}

3. 混合托管! (https://forum.onflow.org/t/hybrid-custody/4016)这是一个 Flow 独有的突破性功能,很快就会发布。敬请关注,它可能会让你大吃一惊!



Cadence 的交易是怎么回事?

Cadence 和 Solidity 的另一个主要区别是,部署的合约不是在虚拟机中执行的唯一代码。Cadence 能够运行脚本,其中一个子集是交易,两者都允许任意代码。脚本或交易不在链上部署,总是存在于链外,然而,它们是被执行运行时执行的顶级代码有效负载。客户端通过 Flow Access API gRPC 或 REST 端点发送脚本和交易,并适时向客户返回结果。脚本和交易能够以更有效、更强大的方式将 dApp 与底层区块链整合在一起,其中合约可以更纯粹地被视为服务或组件,而脚本或交易则成为 dApp 特定的 API 接口,用于链式互动。


脚本在本质上是只读的,只需要一个主函数声明,并对链上的状态进行查询,例如:

// This script reads the balance field of an account's ExampleToken Balanceimport FungibleToken from "../../contracts/FungibleToken.cdc"import ExampleToken from "../../contracts/ExampleToken.cdc"
pub fun main(account: Address): UFix64 {    let acct = getAccount(account)    let vaultRef = acct.getCapability(ExampleToken.VaultPublicPath)        .borrow<&ExampleToken.Vault{FungibleToken.Balance}>()        ?? panic("Could not borrow Balance reference to the Vault")
   return vaultRef.balance}

交易是一个 ACID(Atomic, Consistent, Isolated and Durable)版本的脚本,它只有准备和执行功能,要么完全成功,并按照交易内容改变链状态,要么失败,没有任何改变。它们还支持设置前置和后置条件。在下面的交易实例中,ExampleTokens 被存入 addressAmountMap 中的各个地址的接收器中。

import FungibleToken from "../contracts/FungibleToken.cdc"import ExampleToken from "../contracts/ExampleToken.cdc"
/// Transfers tokens to a list of addresses specified in the `addressAmountMap` parametertransaction(addressAmountMap: {Address: UFix64}) {
   // The Vault resource that holds the tokens that are being transferred    let vaultRef: &ExampleToken.Vault
   prepare(signer: AuthAccount) {
       // Get a reference to the signer's stored vault        self.vaultRef = signer.borrow<&ExampleToken.Vault>(from: ExampleToken.VaultStoragePath)      ?? panic("Could not borrow reference to the owner's Vault!")    }
   execute {
       for address in addressAmountMap.keys {
           // Withdraw tokens from the signer's stored vault            let sentVault <- self.vaultRef.withdraw(amount: addressAmountMap[address]!)
           // Get the recipient's public account object            let recipient = getAccount(address)
           // Get a reference to the recipient's Receiver            let receiverRef = recipient.getCapability(ExampleToken.ReceiverPublicPath)                .borrow<&{FungibleToken.Receiver}>()                ?? panic("Could not borrow receiver reference to the recipient's Vault")
           // Deposit the withdrawn tokens in the recipient's receiver            receiverRef.deposit(from: <-sentVault)
       }    }}

交易可以包括任意数量的提款 / 存款,跨越多个 FT,发送至多个地址,或是其他更复杂的变化,鉴于其 ACID 属性,所有这些交易都将全部成功或失败。


■ 交易后的状态

我们已经介绍了 Solidity 和 Cadence 各自的代币标准的代币转移运行机制,你应该对它们的实现和设计差异有清晰的理解。然而,在这些交易完成后,链上还有需要探究的内容,故事尚未结束。

如前所述 _balances 是 Solidity 合约维护分类账映射条目的地方。钱包将这些余额汇集成一个连贯的单一视图给用户。假设我刚刚用我的 GIBBON 代币完成了一个 transferFrom(),我的钱包将通过调用其 balanceOf() 方法来更新 GIBBON 代币的余额。


由于 0x001 账户拥有多个代币,钱包会根据每个符合 ERC-20 的代币合约查询 balanceOf()。


不出所料,Cadence 利用了账户模型,根据需要为每个不同的代币类型持有一个金库。正因为如此,没有必要让钱包创建一个统一的视图——通过使用 flow-cli 或其他在线工具检查账户,同样可以看到一个账户在所有拥有的代币中的余额。



确定所有权的框架

Solidity 和 Cadence 之间在代币所有权方面的区别可能是他们理念中最重要的对比方面。资源、账户模型和基于能力的访问通过 FT 标准有效地结合在一起,以实现代币所有权,作为以领域为中心的原语,易于推理和扩大建设者的解决方案空间。Solidity 维护所有者的余额记录——类似于银行账户——Cadence 的金库确保代币在你的账户中拥有,就像在你的钱包中拥有现金。对合约接口和功能的访问由与合约互动的人拥有的能力进行管理。事实上,建设者需要适应的哲学也许可以总结为“不是你是谁,而是你拥有什么”,它定义了 Cadence 中的合约互动方式。正如我们在下一篇文章中所探讨的,NFT 的所有权也遵循同样的原则,另外还为管理 NFT 集合提供丰富的语义,为列表和报价提供全网络的可发现性,以及在这一切之下,所有元数据与你的 NFT 一起生活在链上是多么美妙。


■ 即将发布……NFT 转移和可组合性

在这篇文章中,我们探讨了每种语言的同质化代币转移是如何工作的,然后从设计和思维方式一直到数据和计算是如何在链上处理的都进行了对比。我们强调了 Solidity 简单的、基于访问的模型所产生的限制如何被证明是有问题的,这如何影响了可组合性,以及 Cadence 如何提供一个强大的、引人注目的替代方案。随着行业处于一个关键的拐点,现实生活中的实用性和可组合性用例仍然明显低于承诺的未来,Cadence 为建设者提供了一个现实和实用的方法来推动 dApp 工程技术的前沿。现在正在发展的 Cadence 的全球开源工程社区仍然非常专注于为当前和未来几代 Web3 建设者创造最适合的、有效的和安全的智能合约语言。上面分享的许多新的语言功能,以及即将推出的所有突破性的新功能,都包括来自社区的许多重要贡献。


如果你想了解更多关于 Cadence 的信息,或者想参与到社区中来,我们邀请你加入我们的 discord!请查看我们为 Solidity 开发者提供的详细指南(https://developers.flow.com/cadence/solidity-to-cadence)以及为开发者提供的许多其他资源。我们期待着看到你在未来的几年里最终建立了哪些令人兴奋的解决方案,并希望你在 Twitter 上用 #onFlow 标签标记我们!


请继续关注第二部分,我们将深入研究 ERC-721 和相应的 Cadence 标准之间的 NFT 转移的比较!


 END 

什么是 Flow 福洛链?


Flow 福洛链是一个快速,去中心化,且对开发者友好的区块链,旨在为新一代游戏、娱乐应用程序提供动力的数字资产的基础。Flow 是唯一一个由始至终为消费者提供出色体验的 Layer-1 区块链团队。其团队创造的 dApp 包括:CryptoKittiesDapper WalletsNBA Top shot


CrytoKitties 于 2017 年推出时便快速成为加密市场最受欢迎的 dApp,因其成功而导致以太坊堵塞。在 Flow 上运营的 NBA Top shot 也已成为增长最快的 dApp,在公开发布后的 6 个月创造了 7 亿美金销量。正因为 Flow 公链的可扩展性和消费者友好的体验,让这一切成为可能。目前有 1000 多个项目正在 Flow 链上筹备中,我们期待看到一个伟大的生态系统蓬勃发展。


关于 Dapper Labs

Dapper Labs 是一家位于加拿大的全球顶尖区块链服务商,在 2017 年年底通过 CryptoKitties 收藏游戏成功进入⽤户视野,并且因为加密猫的爆⽕导致以太坊拥堵,从而推出 Flow 公链以及全新的开发语言—— Cadence,旨在吸引更多的开发者在 Flow 上开发应⽤。 


Flow 的合作伙伴们:

我们欢迎越来越多的小伙伴加入 Flow 星球,为星球增添色彩!


Flow 官网:https://zh.onflow.org/

Flow 论坛: https://forum.onflow.org/

Flow Discord:

https://discord.com/invite/flow

Flow CN Telegram: https://t.me/flow_zh

Flow B 站:https://space.bilibili.com/1002168058

Flow 微博: 

https://weibo.com/7610419699

Flow CSDN:

https://blog.csdn.net/weixin_57551966?spm=1010.2135.3001.5343


扫码添加 Flow 官方账号微信号,加入 Flow 生态群

微信号 : FlowChainOfficial

【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

福洛链 Flow Official
数据请求中
查看更多

推荐专栏

数据请求中
在 App 打开