之前两篇(浅谈区块链DAPP学习 和 浅谈区块链DAPP学习·续),我们聊了DAPP以及DAPP的一个简单的投票实现,可能还是有很多非技术类的朋友,他们不理解web3.0这样一种可以拥有的网络到底有什么用。
这一篇我准备拿现在国外这几年比较流行DAO聊一下web3.0的未来应用。
DAO是 Decentralized Autonomous Organization 的简写,即去中心化自治组织,有时也被称为分布式自治公司(DAC);有共同的目标或是共识,有明确的核心价值观。它的民主化的投票机制,决定了组织的方向和运作方式。
首先DAO是用智能合约和开源编码的。该组织所做的是完全透明的。决策是通过DAO成员的基层投票做出的。在这样一种公开公平的区块链技术加持下,我们可以在互联网上成立各种不需要见面但可以为一个共同目标而努力的组织。
很魔幻,但在当下疫情肆虐的情况下却有着他生长的基础,元宇宙大概率会在DAO的基础上结合其他技术慢慢成长或是快速成长。
理论上,任何组织都可以是DAO。投资DAO和赠款DAO可以资助项目。收藏家DAO可以收集NFT数字艺术和其他收藏品。社交DAO可以为其成员提供一定的社交圈。
作为技术我不像做预言家,我还是切入我文章的主题,去介绍DAO的技术(DAO Operation Systems),相关的技术蛮多的。
好在区块链要用智能合约管理加密货币,所以你要通过合约审核必须开源,我们能找到很多国外关于DAO的技术,今天我就来介绍一下ARAGON这个项目。
Aragon使用区块链来提高公司和组织的效率和透明度。公司可以使用Aragon彼此签署智能合约协议,然后可以将它们安全地存储在区块链上,以供任何一方在需要时访问。
Aragon喜欢视自己为数字司法管辖区。该网络正在创建一个去中心化的结算系统,该系统可用于在相关各方之间快速有效地进行仲裁。该平台具有自己的Token 令牌 Aragon Network Token - 简称“ANT” - 用于支付费用,并托管在第三方中以提供激励给良好诚实的用户行为。
首先https://aragon.org/是他的官网,你可以上官网创建自己的DAO组织我们当然不会只使用他们官网来建立自己的DAO;使用他的技术架构才是我们的目的。
首先来到ARAGON GITHUB
https://github.com/aragon/
找到aragon-cli项目
https://github.com/aragon/aragon-cli
npm install --global @aragon/cli
npm install --global @aragon/cli@nightly
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;
/// Events
event Increment(address indexed entity, uint256 step);
event Decrement(address indexed entity, uint256 step);
/// State
uint256 public value;
/// ACL
bytes32 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-etherscan
etherscan: {
apiKey: '', // API Key for smart contract verification. Get yours at https://etherscan.io/apis
},
// Aragon plugin configuration
aragon: {
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 } = appState
const pathParts = path.match(/^\/tab\/([0-9]+)/)
const pageIndex = Array.isArray(pathParts)
? parseInt(pathParts[1], 10) - 1
: 0
return (
{isSyncing && }
{count}
}
/>
requestPath(`/tab/${index + 1}`)}
/>
Count: {count}
}
label="Decrement"
onClick={() => api.decrement(1).toPromise()}
/>
}
label="Increment"
onClick={() => api.increment(1).toPromise()}
css={`
margin-left: ${2 * GU}px;
`}
/>
)
}
const Buttons = styled.div`
display: grid;
grid-auto-flow: column;
grid-gap: 40px;
margin-top: 20px;
`
export default App
api.decrement(1).toPromise() api.increment(1).toPromise() 两个方法调用加减来实现合约接口的,点击加号或减号功能就这么简单的完成了,用它改建一个投票是非常简单的。
除了这样一步步,能快速部署aragon的功能官网部分功能吗?可以
mkdir aragon
npm init
npm install @aragon/aragen
npx aragen start
可以看到初始化了一个aragon的ganache环境。
其中就包括了:ENS instance deployed at: 0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1(use-token:用于获取以太坊上代币相关信息的 React 实用程序,包括相关合约)。
首先来到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)?.type
const 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_DEFAULT
return lastNetwork
}, [])
const [walletWeb3, setWalletWeb3] = useState(null)
const [disconnectedNetworkType, setDisconnectedNetworkType] = useState(
initialNetwork
)
const connected = useMemo(() => status === 'connected', [status])
const networkType = useMemo(() => {
const newNetwork = connected ? networkName : disconnectedNetworkType
LocalStorageWrapper.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 changes
useEffect(() => {
let cancel = false
if (!ethereum) {
return
}
const walletWeb3 = getWeb3(ethereum)
if (!cancel) {
setWalletWeb3(walletWeb3)
}
return () => {
cancel = true
setWalletWeb3(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 connected
connected,
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 (
{children}
)
}
WalletContextProvider.propTypes = { children: PropTypes.node }
export function WalletProvider({ children }) {
return (
{children}
)
}
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)
文件地址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([
[
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 build
build完成把dist目录下的文件复制到client项目node_modules/use-wallet/dist/下
src/network-config.js把 isActive: false,改成isActive: true,
ensRegistry: localEnsRegistryAddress,要改成(ensRegistry: localEnsRegistryAddress||"0x5f6f7e8cc7346a11ca2def8f827b7a0b612c56a1")
[chains.getChainInformation(1337).type]: {
isActive: false,//就是这里
addresses: {
ensRegistry: localEnsRegistryAddress,
governExecutorProxy: null,
},
nodes: {
defaultEth: 'ws://localhost:8545',
},
connectGraphEndpoint: null,
settings: {
// Local development environments by convention use
// a chainId of value 1337, but for the sake of configuration
// we expose a way to change this value.
chainId: 1337,
testnet: true,
...chains.getChainInformation(1337),
live: false,
},
},
我们发现Testnets多了EDC,然后本地链的钱包链上了,可以尽情的玩他的功能不用胆心加密币不够了。
点击 create
点击 Use this template
点击 next
点击 next 输入一个本地账户地址
继续 next
对的在这个组织里做什么都需要投票通过就算你转账也需要全员投票哦。
以上只是简单介绍了aragon部署,我们刚才没有用company原因我们没有部署合约;
合约可以在dao-templates里下载部署有机会后面可以写一下;其实项目里的配置文件写的很清楚。
https://github.com/aragon/dao-templates
还有就是我们用的都是aragon默认的ganache环境可以改吗?也可以aragen下载源码;
https://github.com/aragon/aragen
web3.0是python2的nodejs版本选择很重要;
项目会自动下载所有主要项目,其中几个项目的环境有差异,但可以分块切换环境部署,
还有就是ipfs其实很多图片都存在ipfs里需要本地部署的通过aragon项目发布到ipfs本地服务上;
ipfs官网地址,
https://docs.ipfs.io/install/command-line/#official-distributions
不多说了,其实还有很多技术细节还没写完,本人技术也有限,有什么错漏的希望大家指正。