从Dao聊到Aragon
前言
之前兩篇(淺談區塊鏈DAPP學習?和 淺談區塊鏈DAPP學習·續),我們聊了DAPP以及DAPP的一個簡單的投票實現,可能還是有很多非技術類的朋友,他們不理解web3.0這樣一種可以擁有的網絡到底有什么用。
這一篇我準備拿現在國外這幾年比較流行DAO聊一下web3.0的未來應用。
首先什么是DAO
DAO是 Decentralized Autonomous Organization 的簡寫,即去中心化自治組織,有時也被稱為分布式自治公司(DAC);有共同的目標或是共識,有明確的核心價值觀。它的民主化的投票機制,決定了組織的方向和運作方式。
DAO的意義
首先DAO是用智能合約和開源編碼的。該組織所做的是完全透明的。決策是通過DAO成員的基層投票做出的。在這樣一種公開公平的區塊鏈技術加持下,我們可以在互聯網上成立各種不需要見面但可以為一個共同目標而努力的組織。
很魔幻,但在當下疫情肆虐的情況下卻有著他生長的基礎,元宇宙大概率會在DAO的基礎上結合其他技術慢慢成長或是快速成長。
DAO的項目
理論上,任何組織都可以是DAO。投資DAO和贈款DAO可以資助項目。收藏家DAO可以收集NFT數字藝術和其他收藏品。社交DAO可以為其成員提供一定的社交圈。
DAO Operation Systems
作為技術我不像做預言家,我還是切入我文章的主題,去介紹DAO的技術(DAO Operation Systems),相關的技術蠻多的。
好在區塊鏈要用智能合約管理加密貨幣,所以你要通過合約審核必須開源,我們能找到很多國外關于DAO的技術,今天我就來介紹一下ARAGON這個項目。
什么是ARAGON
Aragon使用區塊鏈來提高公司和組織的效率和透明度。公司可以使用Aragon彼此簽署智能合約協議,然后可以將它們安全地存儲在區塊鏈上,以供任何一方在需要時訪問。
Aragon喜歡視自己為數字司法管轄區。該網絡正在創建一個去中心化的結算系統,該系統可用于在相關各方之間快速有效地進行仲裁。該平臺具有自己的Token 令牌 Aragon Network Token - 簡稱“ANT” - 用于支付費用,并托管在第三方中以提供激勵給良好誠實的用戶行為。
如何開始一個ARAGON
首先https://aragon.org/是他的官網,你可以上官網創建自己的DAO組織我們當然不會只使用他們官網來建立自己的DAO;使用他的技術架構才是我們的目的。
快速搭建ARAGON DAO本地架構
首先來到ARAGON GITHUB
https://github.com/aragon/找到aragon-cli項目
https://github.com/aragon/aragon-cli首先全局安裝aragon/cli
npm install --global @aragon/cli npm install --global @aragon/cli@nightly創建你第一個ARAGON DAO項目
npx create-aragon-app myapp cd myapp npm start項目打開后會用默認瀏覽器打開 http://localhost:3000/可以看到啟動了一個ganache初始化了8個賬號;以及ENS,DAO APM等的address;簡單的說他初始化了一個aragon的本地環境。從右上角看默認鏈接了本地ganache的錢包,同時已經顯示了本地錢包的地址。
代碼分析
看了以后大家會發現和我們以前的結構很類似contracts下有個CounterApp.sol合約
pragma solidity ^0.4.24;import "@aragon/os/contracts/apps/AragonApp.sol"; import "@aragon/os/contracts/lib/math/SafeMath.sol";contract CounterApp is AragonApp {using SafeMath for uint256;/// Eventsevent Increment(address indexed entity, uint256 step);event Decrement(address indexed entity, uint256 step);/// Stateuint256 public value;/// ACLbytes32 constant public INCREMENT_ROLE = keccak256("INCREMENT_ROLE");bytes32 constant public DECREMENT_ROLE = keccak256("DECREMENT_ROLE");function initialize(uint256 _initValue) public onlyInit {value = _initValue;initialized();}/*** @notice Increment the counter by `step`* @param step Amount to increment by*/function increment(uint256 step) external auth(INCREMENT_ROLE) {value = value.add(step);emit Increment(msg.sender, step);}/*** @notice Decrement the counter by `step`* @param step Amount to decrement by*/function decrement(uint256 step) external auth(DECREMENT_ROLE) {value = value.sub(step);emit Decrement(msg.sender, step);} }這個就是一個計數合約加減兩個功能,其實和原來投票幾乎一樣,少了個身份認證,可以從上一篇加進來相關代碼來不足。
scripts目錄下 buidler-hooks.js 這些鉤子由 Aragon Buidler 插件在start任務的生命周期中調用的,結合buidler配置文件(buidler.config.js)部署和掛鉤合約。
const { usePlugin } = require('@nomiclabs/buidler/config') const hooks = require('./scripts/buidler-hooks')usePlugin('@aragon/buidler-aragon')module.exports = {// Default Buidler configurations. Read more about it at https://buidler.dev/config/defaultNetwork: 'localhost',networks: {localhost: {url: 'http://localhost:8545',},},solc: {version: '0.4.24',optimizer: {enabled: true,runs: 10000,},},// Etherscan plugin configuration. Learn more at https://github.com/nomiclabs/buidler/tree/master/packages/buidler-etherscanetherscan: {apiKey: '', // API Key for smart contract verification. Get yours at https://etherscan.io/apis},// Aragon plugin configurationaragon: {appServePort: 8001,clientServePort: 3000,appSrcPath: 'app/',appBuildOutputPath: 'dist/',appName: 'myapp',hooks, // Path to script hooks}, }buidler.config.js代碼可見默認是鏈接本地localhost的ganache 并部署相關合約。
src目錄下的App.js
import { useAragonApi } from '@aragon/api-react'const { api, appState, path, requestPath } = useAragonApi() const { count, isSyncing } = appState很清楚是通過 useAragonApi 去獲得 api 方法和 appState 的 count 值的 附上 app.js 源碼
import React from 'react' import { useAragonApi } from '@aragon/api-react' import {Box,Button,GU,Header,IconMinus,IconPlus,Main,SyncIndicator,Tabs,Text,textStyle, } from '@aragon/ui' import styled from 'styled-components'function App() {const { api, appState, path, requestPath } = useAragonApi()const { count, isSyncing } = appStateconst pathParts = path.match(/^\/tab\/([0-9]+)/)const pageIndex = Array.isArray(pathParts)? parseInt(pathParts[1], 10) - 1: 0return (<Main>{isSyncing && <SyncIndicator />}<Headerprimary="Counter"secondary={<spancss={`${textStyle('title2')}`}>{count}</span>}/><Tabsitems={['Tab 1', 'Tab 2']}selected={pageIndex}onChange={index => requestPath(`/tab/${index + 1}`)}/><Boxcss={`display: flex;align-items: center;justify-content: center;text-align: center;height: ${50 * GU}px;${textStyle('title3')};`}>Count: {count}<Buttons><Buttondisplay="icon"icon={<IconMinus />}label="Decrement"onClick={() => api.decrement(1).toPromise()}/><Buttondisplay="icon"icon={<IconPlus />}label="Increment"onClick={() => api.increment(1).toPromise()}css={`margin-left: ${2 * GU}px;`}/></Buttons></Box></Main>) }const Buttons = styled.div`display: grid;grid-auto-flow: column;grid-gap: 40px;margin-top: 20px; `export default Appapi.decrement(1).toPromise() api.increment(1).toPromise() 兩個方法調用加減來實現合約接口的,點擊加號或減號功能就這么簡單的完成了,用它改建一個投票是非常簡單的。
部署aragon
除了這樣一步步,能快速部署aragon的功能官網部分功能嗎?可以
mkdir aragon npm init npm install @aragon/aragen npx aragen start可以看到初始化了一個aragon的ganache環境。
其中就包括了:ENS instance deployed at: 0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1(use-token:用于獲取以太坊上代幣相關信息的 React 實用程序,包括相關合約)。
啟動client項目
首先來到ARAGON GITHUB找到Aragon Client
https://github.com/aragon/client下載項目進入項目根目錄
yarn npm run start:local服務啟動在:http://localhost:3000 在項目根目錄下有個arapp.json里面的內容默認的是rpc網絡也就是本地網絡localhost:8545
"environments": {"default": {"registry": "0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1","appName": "aragon.aragonpm.eth","network": "rpc"},他的registry address和aragon啟動的網絡是一樣的
現在打開http://localhost:3000
改造錢包
右上角你會發現錢包無法連接本地鏈; 看來我們要改造他們項目的錢包了。 廢話少說,在項目src/contexts/目錄下有一個wellet.js附上代碼
import React, {useContext,useEffect,useMemo,useCallback,useState, } from 'react' import PropTypes from 'prop-types' import BN from 'bn.js' import {useWallet as useWalletBase,UseWalletProvider,ChainUnsupportedError,chains, } from 'use-wallet' import { getWeb3, filterBalanceValue } from '../util/web3' import { useWalletConnectors } from '../ethereum-providers/connectors' import { useAPM, updateAPMContext } from './elasticAPM' import { LocalStorageWrapper } from '../local-storage-wrapper'export const WALLET_STATUS = Object.freeze({providers: 'providers',connecting: 'connecting',connected: 'connected',disconnected: 'disconnected',error: 'error', })// default network is mainnet if user is not connected const NETWORK_TYPE_DEFAULT = chains.getChainInformation(1)?.typeconst WalletContext = React.createContext()function WalletContextProvider({ children }) {const {account,balance,ethereum,connector,status,chainId,providerInfo,type,networkName,...walletBaseRest} = useWalletBase()const initialNetwork = useMemo(() => {const lastNetwork = LocalStorageWrapper.get('last-network', false)if (!lastNetwork) return NETWORK_TYPE_DEFAULTreturn lastNetwork}, [])const [walletWeb3, setWalletWeb3] = useState(null)const [disconnectedNetworkType, setDisconnectedNetworkType] = useState(initialNetwork)const connected = useMemo(() => status === 'connected', [status])const networkType = useMemo(() => {const newNetwork = connected ? networkName : disconnectedNetworkTypeLocalStorageWrapper.set('last-network', newNetwork, false)return newNetwork}, [connected, networkName, disconnectedNetworkType])const changeNetworkTypeDisconnected = useCallback(newNetworkType => {if (status === 'disconnected') {setDisconnectedNetworkType(newNetworkType)}},[status])// get web3 and set local storage prefix whenever networkType changesuseEffect(() => {let cancel = falseif (!ethereum) {return}const walletWeb3 = getWeb3(ethereum)if (!cancel) {setWalletWeb3(walletWeb3)}return () => {cancel = truesetWalletWeb3(null)}}, [ethereum, networkType])const wallet = useMemo(() => ({account,balance: new BN(filterBalanceValue(balance)),ethereum,networkType,providerInfo: providerInfo,web3: walletWeb3,status,chainId: connected ? chainId : 1, // connect to mainnet if wallet is not connectedconnected,changeNetworkTypeDisconnected,...walletBaseRest,}),[account,balance,ethereum,networkType,providerInfo,status,chainId,walletBaseRest,walletWeb3,connected,changeNetworkTypeDisconnected,])const { apm } = useAPM()useEffect(() => {updateAPMContext(apm, wallet.networkType)}, [apm, wallet.networkType])return (<WalletContext.Provider value={wallet}>{children}</WalletContext.Provider>) } WalletContextProvider.propTypes = { children: PropTypes.node }export function WalletProvider({ children }) {return (<UseWalletProvider connectors={useWalletConnectors} autoConnect><WalletContextProvider>{children}</WalletContextProvider></UseWalletProvider>) } WalletProvider.propTypes = { children: PropTypes.node }export function useWallet() {return useContext(WalletContext) }export { ChainUnsupportedError }連接本地錢包
經過一番仔細的閱讀發覺錢包掉用的是'use-wallet',而且判斷是否可以鏈接的代碼在被調用的modules里,只有改了它才能本地部署aragon的本地client;use-wallet的源碼地址
文件位置src/connectors/ConnectorInjected.ts
#添加的代碼就這一段;這個就是添加錢包可以鏈接的本地chainId chainId.push(1337)配置本地EDC鏈
文件地址src/chains.ts,添加以下代碼:
const EDC: Currency = {name: 'Ether',symbol: 'ETH',decimals: 18, } .....................[1337,{id: 1337,nativeCurrency: EDC,fullName: 'EDC',shortName: 'EDC',type: 'local',testnet: true,},],附上完整代碼
import { ChainUnknownError } from './errors' import { ChainInformation, ChainType, Currency } from './types'const ETH: Currency = {name: 'Ether',symbol: 'ETH',decimals: 18, }const MATIC: Currency = {name: 'Matic Token',symbol: 'MATIC',decimals: 18, }const AVAX: Currency = {name: 'Avax',symbol: 'AVAX',decimals: 9, }const ONE: Currency = {name: 'ONE Token',symbol: 'ONE',decimals: 18, }const XDAI: Currency = {name: 'xDAI',symbol: 'xDAI',decimals: 18, }const BNB: Currency = {name: 'Binance Token',symbol: 'BNB',decimals: 18, }const TT: Currency = {name: 'Thunder Token',symbol: 'TT',decimals: 18, }const CELO: Currency = {name: 'Celo',symbol: 'CELO',decimals: 18, }const METIS: Currency = {name: 'METIS',symbol: 'METIS',decimals: 18, }const FTM: Currency = {name: 'FTM',symbol: 'FTM',decimals: 18, }const DEV: Currency = {name: 'DEV',symbol: 'DEV',decimals: 18, } const MOVR: Currency = {name: 'Moonriver',symbol: 'MOVR',decimals: 18, } const GLMR: Currency = {name: 'Glimmer',symbol: 'GLMR',decimals: 18, } const EDC: Currency = {name: 'Ether',symbol: 'ETH',decimals: 18, } const CHAIN_INFORMATION = new Map<number, ChainInformation | ChainType>([[1,{id: 1,nativeCurrency: ETH,type: 'main',fullName: 'Ethereum Mainnet',shortName: 'Ethereum',explorerUrl: `https://etherscan.io`,testnet: false,},],[3,{id: 3,nativeCurrency: ETH,type: 'ropsten',fullName: 'Ropsten Testnet',shortName: 'Ropsten',explorerUrl: `https://ropsten.etherscan.io`,testnet: true,},],[4,{id: 4,nativeCurrency: ETH,type: 'rinkeby',fullName: 'Rinkeby Testnet',shortName: 'Rinkeby',explorerUrl: `https://rinkeby.etherscan.io`,testnet: true,},],[5,{id: 5,nativeCurrency: ETH,type: 'goerli',fullName: 'Goerli Testnet',shortName: 'Goerli',explorerUrl: `https://goerli.etherscan.io`,testnet: true,},],[42,{id: 42,nativeCurrency: ETH,type: 'kovan',fullName: 'Kovan Testnet',shortName: 'Kovan',explorerUrl: `https://kovan.etherscan.io`,testnet: true,},],[43112,{id: 43112,nativeCurrency: AVAX,type: 'avalocal',shortName: 'Avalanche Local',fullName: 'Avalanche Local',testnet: true,},],[43113,{id: 43113,nativeCurrency: AVAX,type: 'fuji',fullName: 'Avalanche Fuji',shortName: 'Fuji',explorerUrl: 'https://testnet.snowtrace.io/',testnet: true,},],[43114,{id: 43114,nativeCurrency: AVAX,type: 'avalanche',fullName: 'Avalanche Mainnet',shortName: 'Avalanche',explorerUrl: 'https://snowtrace.io/',testnet: false,},],[100,{id: 100,nativeCurrency: XDAI,type: 'xdai',fullName: 'xDAI',shortName: 'xDAI',explorerUrl: 'https://blockscout.com/xdai/mainnet/',testnet: false,},],[137,{id: 137,nativeCurrency: MATIC,type: 'matic',fullName: 'Polygon Mainnet',shortName: 'Polygon',explorerUrl: `https://polygonscan.com`,testnet: false,},],[80001,{id: 80001,nativeCurrency: MATIC,type: 'mumbai',fullName: 'Mumbai Testnet',shortName: 'Mumbai',explorerUrl: `https://mumbai.polygonscan.com`,testnet: true,},],[250,{id: 250,nativeCurrency: FTM,type: 'fantom',fullName: 'Fantom Opera Mainnet',shortName: 'FTM',explorerUrl: `https://ftmscan.com/`,testnet: false,},],[1666600000,{id: 1666600000,nativeCurrency: ONE,type: 'harmony',fullName: 'Harmony ONE',shortName: 'Harmony',explorerUrl: `https://explorer.harmony.one/`,testnet: false,},],[1666700000,{id: 1666700000,nativeCurrency: ONE,type: 'harmonyTest',fullName: 'Harmony ONE Testnet',shortName: 'Harmony Testnet',explorerUrl: `https://explorer.testnet.harmony.one/`,testnet: true,},],[56,{id: 56,nativeCurrency: BNB,type: 'bsc',fullName: 'Binance Smart Chain',shortName: 'BSC',explorerUrl: `https://bscscan.com/`,testnet: false,},],[97,{id: 97,nativeCurrency: BNB,type: 'bscTest',fullName: 'Binance Smart Chain Testnet',shortName: 'BSC Testnet',explorerUrl: `https://testnet.bscscan.com/`,testnet: true,},],[108,{id: 108,nativeCurrency: TT,type: 'thundercore',fullName: 'ThunderCore Mainnet',shortName: 'ThunderCore',explorerUrl: `https://scan.thundercore.com/`,testnet: false,},],[18,{id: 18,nativeCurrency: TT,type: 'thundercoreTest',fullName: 'ThunderCore Testnet',shortName: 'ThunderCore Testnet',explorerUrl: `https://scan-testnet.thundercore.com/`,testnet: true,},],[421611,{id: 421611,nativeCurrency: ETH,type: 'arbitrumTest',fullName: 'Arbitrum Testnet',shortName: 'Arbitrum Testnet',explorerUrl: 'https://testnet.arbiscan.io/',testnet: true,},],[42161,{id: 42161,nativeCurrency: ETH,type: 'arbitrum',fullName: 'Arbitrum Mainnet',shortName: 'Arbitrum',explorerUrl: 'https://arbiscan.io/',testnet: false,},],[42220,{id: 42220,nativeCurrency: CELO,type: 'celo',fullName: 'Celo (Mainnet)',shortName: 'Celo',explorerUrl: 'https://explorer.celo.org/',testnet: false,},],[44787,{id: 44787,nativeCurrency: CELO,type: 'celoTest',fullName: 'Celo (Alfajores Testnet)',shortName: 'Alfajores',explorerUrl: 'https://alfajores-blockscout.celo-testnet.org/',testnet: true,},],[588,{id: 588,nativeCurrency: METIS,type: 'stardust',fullName: 'Metis Stardust Testnet',shortName: 'Stardust',explorerUrl: 'https://stardust-explorer.metis.io/',testnet: true,},],[1088,{id: 1088,nativeCurrency: METIS,type: 'andromeda',fullName: 'Metis Andromeda',shortName: 'Andromeda',explorerUrl: 'https://andromeda-explorer.metis.io/',testnet: false,},],[1313161555,{id: 1313161555,nativeCurrency: ETH,type: 'aurora',fullName: 'Aurora Testnet',shortName: 'AuroraTest',explorerUrl: 'https://explorer.testnet.aurora.dev/',testnet: true,},],[1313161554,{id: 1313161554,nativeCurrency: ETH,type: 'aurora',fullName: 'Aurora Mainnet',shortName: 'Aurora',explorerUrl: 'https://explorer.mainnet.aurora.dev/',testnet: false,},],[1287,{id: 1287,nativeCurrency: DEV,type: 'moonbase',fullName: 'moonbase',shortName: 'Moonbase Alphanet',explorerUrl: 'https://moonbase.moonscan.io/',testnet: true,},],[1285,{id: 1285,nativeCurrency: MOVR,type: 'moonriver',fullName: 'Moonriver',shortName: 'Moonriver',explorerUrl: 'https://moonriver.moonscan.io/',testnet: false,},],[1284,{id: 1284,nativeCurrency: GLMR,type: 'moonbeam',fullName: 'Moonbeam',shortName: 'Moonbeam',explorerUrl: 'https://moonbeam.moonscan.io/',testnet: false,},],[1337,{id: 1337,nativeCurrency: EDC,fullName: 'EDC',shortName: 'EDC',type: 'local',testnet: true,},],[5777,{id: 5777,type: 'ganache',testnet: true,},], ])/*** This method checks whether a particular chain id is known.** @param {number} chainId chain id to check* @returns {boolean} true if chain is known*/ export function isKnownChain(chainId: number): boolean {return CHAIN_INFORMATION.has(chainId) }/**** @param {number} chainId chain id to retrieve information for* @throws {ChainUnknownError} if chain is unknown* @returns {boolean} information for specified chain*/ export function getChainInformation(chainId: number ): ChainInformation | ChainType {const chainInfo = CHAIN_INFORMATION.get(chainId)if (!chainInfo) throw new ChainUnknownError(`Unknown chain id: ${chainId}`)return chainInfo }/*** This is a getter method to returns the chain ids of all known chains.** @returns {number[]} array of chain Ids*/ export function getKnownChainsIds(): number[] {return Array.from(CHAIN_INFORMATION.keys()) }/*** This is a getter method to return all information available for each known chain.** @returns {ChainInformation | ChainType[]} An array containing information for* each known chain*/ export function getKnownChainInformation(): ChainInformation | ChainType[] {return Array.from(CHAIN_INFORMATION.values()) }export function getDefaultChainId(): number {return 1 }開始編譯
yarn run buildbuild完成把dist目錄下的文件復制到client項目node_modules/use-wallet/dist/下
配置client項目
src/network-config.js把 isActive: false,改成isActive: true,
ensRegistry: localEnsRegistryAddress,要改成(ensRegistry: localEnsRegistryAddress||"0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1")
運行前端頁面
我們發現Testnets多了EDC,然后本地鏈的錢包鏈上了,可以盡情的玩他的功能不用膽心加密幣不夠了。
創建本地組織
下面演示本地創建一個組織的過程:
點擊 create
點擊membership
點擊 Use this template
點擊 next
點擊 next 輸入一個本地賬戶地址
繼續 next
投票和轉賬功能
對的在這個組織里做什么都需要投票通過就算你轉賬也需要全員投票哦。
小結
以上只是簡單介紹了aragon部署,我們剛才沒有用company原因我們沒有部署合約;
合約可以在dao-templates里下載部署有機會后面可以寫一下;其實項目里的配置文件寫的很清楚。
還有就是我們用的都是aragon默認的ganache環境可以改嗎?也可以aragen下載源碼;
web3.0是python2的nodejs版本選擇很重要;
項目會自動下載所有主要項目,其中幾個項目的環境有差異,但可以分塊切換環境部署,
還有就是ipfs其實很多圖片都存在ipfs里需要本地部署的通過aragon項目發布到ipfs本地服務上;
ipfs官網地址,
不多說了,其實還有很多技術細節還沒寫完,本人技術也有限,有什么錯漏的希望大家指正。
總結
以上是生活随笔為你收集整理的从Dao聊到Aragon的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mediainfo工具查看文件信息
- 下一篇: Java ArrayList应用之抽奖软