我的第二场黑客马拉松是在 Flow 链上。 你可能会问为什么我专门选择 Flow 而不是任何其他黑客马拉松? 答案很简单。 我从一开始就是社区的一部分; 在 ICO 期间,当我第一次听说这个项目并以每个代币 60 美分的价格投资于 Flow 时。 这是一笔划算的交易,尤其是当 Flow 的成本上涨到每枚代币 40 美元时。 当时,所有代币都被锁定,ICO 投资者无法出售任何代币。 我只能以 17 美元的价格出售它们,在我看来,这已经很棒了。 从那时起,我就积极关注社区。 我想参加黑客马拉松第一季,但忙于其他几个项目。 所以,当我听说黑客马拉松第二季即将到来时,我非常兴奋地带着我长期以来的慈善想法参加它。
由于这是我第一次接触 Flow 生态系统,所以我决定使用官方指南来学习 Cadence 和 FCL - Flow Client Library,我们稍后会谈到。
官方指南帮助我更好地了解 Cadence,指导我完成最常见的用例。 虽然非常有用,但我发现它有点冗长,写了很多文章以便读者可以随时进入。 这导致了重复并且显得笨拙。 除此之外,我大约花了 6-8 个小时完成,对使用 Flow 生态系统的区块链项目的语言原理和架构有了基本的了解。 Cadence 和 FCL 均由 DapperLabs 开发,提供一致的开发人员体验。 他们的 API 对我来说似乎也很成熟,尽管它是一种新语言,API 将来可能会发生变化,涵盖了开发人员可能需要的几乎所有内容,它可能适用于企业规模的项目。我的项目较小,但仍然需要 很多智能合约,我将在架构部分进行描述。
对于那些不知道的人来说,Cadence 是 Dapper Labs 编写的一种新的智能合约编程语言。
我很想尝试 Cadence,因为不久前听说过这种语言,它的资源导向性质类似于 Rust。 将它与我使用过的成熟的 Solidity 进行比较。
Cadence 的突出特点之一是其面向资源的性质,与编程语言 Rust 相似。 Rust 因其以借贷为中心的高效资源分配机制而闻名。 同样,Cadence 采用了类似的原则:资源一次只能存在于一个位置。 这一基本概念保证了资源安全,特别是智能合约生态系统中的代币和其他关键资源。 这种设计选择不仅增强了安全性,还有助于提高智能合约的可预测性和可靠性。
Cadence 面向资源的方法本质上解决了困扰基于 Solidity 的合约的一些安全问题。 通过防止资源重复和执行严格的所有权规则,Cadence 最大限度地减少了重入攻击和未经授权的令牌传输等漏洞的可能性。 这种对安全性的关注与 Dapper Labs 的愿景是一致的,即为构建去中心化应用程序提供一个强大的平台,同时又不影响安全性。 另一方面,Solidity 的设计允许存在重入攻击等陷阱,这构成了重大的安全威胁。
** 让我们简要检查一下我的黑客马拉松项目的应用程序架构和主要概念。 **
对于我自己的项目来说,Svelte 是理所当然的,所以:
pnmp 出于 obv 原因。
Svelte 作为 js 框架。
SvelteKit 作为应用程序框架。 (对于那些不熟悉 SvelteKit 的人,您可能会认为它类似于 Next.js)
TailwindCSS/DaisyUI - 用于快速原型设计。 (TailwindCSS 用于样式,Tailwind 混合样式库 DaisyUI 用于在 Tailwind shugar 之上提供更多样式,用于按钮、范围等基本组件。)
//package.json{ ..., "devDependencies": { "@sveltejs/adapter-vercel": "^3.0.2", "@sveltejs/kit": "^1.22.1", "autoprefixer": "^10.4.14", "daisyui": "^3.1.11", "eslint": "^8.44.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-svelte": "^2.32.2", "postcss": "^8.4.25", "prettier": "^3.0.0", "prettier-plugin-svelte": "^2.10.1", "svelte": "^4.0.5", "tailwindcss": "^3.3.2", "vite": "^4.4.2" }, "dependencies": { "@onflow/fcl": "^1.4.1", "firebase": "^10.0.0", "nanoid": "^4.0.2", "ramda": "^0.29.0" }}
如您所见,我正在使用 4 个客户端库:
fcl - 流客户端 API
firebase - 我的用户数据存储解决方案 + onSnapshot db 实时更新
nanoid - 生成随机 ID
ramda - 类似于 lodash,但是实用程序的函数式库(Ramda 帮助利用了实用程序函数,如管道、组合等)
因为 svelte 首先是一个编译器,所以 devDependency 和依赖项之间没有真正的区别,但为了概念分离,我添加了客户端库作为依赖项,因此更容易阅读。
您可能想查看具有 localStorage 同步功能的简单 svelte store firebase api 包装器
import {getFirestore, doc, setDoc, getDoc} from 'firebase/firestore';import {initializeApp} from 'firebase/app';import {writable} from 'svelte/store';import {browser} from '$app/environment';import {firebaseKeys} from '$lib/firebase/config';const initialState = {};function createDb(key) { const initialValue = browser && localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key)) : initialState; const {subscribe, set, update} = writable(initialValue); const ini = initializeApp(firebaseKeys); const db = getFirestore(ini); return { set, update, subscribe, getDbRef: () => db, getDoc: async (path, id) => { const docRef = doc(db, path, id); return await getDoc(docRef); }, setDoc: async (path, id, payload) => { try { const docRef = doc(db, path, id); await setDoc(docRef, {...payload}, {merge: true}); console.warn('Document added to Firestore:', document); } catch (error) { console.error('Error adding document to Firestore:', error); } }, useLocalStorage: () => { subscribe((current) => { if (browser) { localStorage.setItem(key, JSON.stringify(current)); } }); } };}export const db = createDb('app-db');
我编写这个包装器是为了简化整个应用程序中 firebase api 的使用。
项目商店可能看起来有点复杂,但逻辑与zustand类似。
我最喜欢的一个方面是能够使用 FCL 集成智能合约交易和脚本,这对于创建无服务器应用程序非常方便。例如,传输代币如下:
...export const transferFlow = async (amount, addr, cid) => { let transactionId = false; initTransactionState(); try { transactionId = await fcl.mutate({ cadence: ` import FungibleToken from 0x9a0766d93b6608b7 import FlowToken from 0x7e60df042a9c0868 transaction(amount: UFix64, to: Address) { // The Vault resource that holds the tokens that are being transferred let sentVault: @FungibleToken.Vault prepare(signer: AuthAccount) { // Get a reference to the signer's stored vault let vaultRef = signer.borrow<&FlowToken.Vault>(from: /storage/flowTokenVault) ?? panic("Could not borrow reference to the owner's Vault!") // Withdraw tokens from the signer's stored vault self.sentVault <- vaultRef.withdraw(amount: amount) } execute { // Get a reference to the recipient's Receiver let receiverRef = getAccount(to) .getCapability(/public/flowTokenReceiver) .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.sentVault) } } `, args: (arg, t) => [arg(amount, t.UFix64), arg(addr, t.Address)], payer: fcl.authz, proposer: fcl.authz, authorizations: [fcl.authz], limit: 50 }); // store method txId.set(transactionId); // subscribe to svelte transactions fcl.tx(transactionId).subscribe((res) => { transactionStatus.set(res.status); if (res.status) { if (res.status === 4) { getAccountBalance(addr, cid); auth.addFlowTransaction({ txId: transactionId, event: `${amount} Flow transferred to ${addr} at`, status: res.status, timestamp: new Date().getTime() }); setTimeout(() => transactionInProgress.set(false), 2000); } } }); } catch (e) { transactionStatus.set(99); console.warn(e); }};...
这里的 fcl.mutate 方法接收具有键节奏和值的对象将是有效的节奏代码,这样您就可以轻松地从 js 文件编写事务和脚本。请参阅 github 中的 flc 操作的更多示例。
总的来说,使用 Flow 生态系统是令人愉快的。 与以太坊生态系统相比,它显得更加简洁和简单。 然而,公平地说,我上次使用 Solidity 生态系统大约是在三年前,所以情况可能已经发生了变化,尽管我对此表示怀疑。
尽管我没有获奖,但使用 Flow 生态系统还是令人愉快的。 与以太坊生态系统相比,它显得更加简洁和简单。 然而,公平地说,我上次使用 Solidity 生态系统大约是在三年前,所以情况可能已经发生了变化,尽管我对此表示怀疑。