關鍵要點
- Story DAO 利用 OpenZeppelin 的 Ownable 合約確保只有所有者才能執行管理功能,從而增強了對 DApp 操作的安全性和控制。
- Story DAO 合約具有可調整的費用和持續時間參數,並設有安全措施以防止未經授權的更改,確保只有所有者才能修改關鍵設置。
- Story DAO 中的白名單管理通過需要付費的功能來實現,允許根據發送者的貢獻自動和有條件地訪問。
- 綜合測試策略,包括 Solidity 和 JavaScript 測試,對於驗證 Story DAO 的功能和安全性至關重要,確保在部署之前穩健運行。
- Story DAO 的部署過程通過 Truffle 簡化,具有特定的遷移腳本和配置,方便從開發環境到生產環境的順利過渡。
本教程系列的第三部分介紹了使用以太坊構建 DApp,我們構建並將令牌部署到以太坊測試網絡 Rinkeby。在本部分中,我們將開始編寫 Story DAO 代碼。
我們將使用介紹性文章中列出的條件來指導我們。
合約概述
讓我們創建一個新的合約 StoryDao.sol,其框架如下:
pragma solidity ^0.4.24; import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol"; import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol"; contract StoryDao is Ownable { using SafeMath for uint256; mapping(address => bool) whitelist; uint256 public whitelistedNumber = 0; mapping(address => bool) blacklist; event Whitelisted(address addr, bool status); event Blacklisted(address addr, bool status); uint256 public daofee = 100; // 百分之几,即 100 为 1% uint256 public whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币 event SubmissionCommissionChanged(uint256 newFee); event WhitelistFeeChanged(uint256 newFee); uint256 public durationDays = 21; // 故事章节持续时间(天) uint256 public durationSubmissions = 1000; // 故事章节持续时间(条目) function changedaofee(uint256 _fee) onlyOwner external { require(_fee <= 1000); // 限制最大费用为 10% daofee = _fee; emit SubmissionCommissionChanged(_fee); } function changewhitelistfee(uint256 _fee) onlyOwner external { require(_fee > 0); // 确保费用大于 0 whitelistfee = _fee; emit WhitelistFeeChanged(_fee); } function changeDurationDays(uint256 _days) onlyOwner external { require(_days >= 1); durationDays = _days; } function changeDurationSubmissions(uint256 _subs) onlyOwner external { require(_subs > 99); durationSubmissions = _subs; } }
我們導入 SafeMath 以再次進行安全計算,但這次我們還使用 Zeppelin 的 Ownable 合約,它允許某人“擁有”故事並執行某些僅限管理員的功能。簡單地說,我們的 StoryDao 是 Ownable 就足夠了;隨意檢查合約以了解其工作原理。
我們還使用此合約中的 onlyOwner 修飾符。函數修飾符基本上是函數的擴展、插件。 onlyOwner 修飾符如下所示:
modifier onlyOwner() { require(msg.sender == owner); _; }
當 onlyOwner 添加到函數時,該函數的主體將粘貼到 _; 部分所在的位置,並且之前的部分將首先執行。因此,通過使用此修飾符,該函數會自動檢查消息發送者是否是合約的所有者,然後如果屬實則照常繼續。如果不是,則會崩潰。
通過在更改 Story DAO 的費用和其他參數的函數上使用 onlyOwner 修飾符,我們確保只有管理員才能進行這些更改。
測試
讓我們測試初始函數。
如果不存在,請創建文件夾 test。然後在其中創建文件 TestStoryDao.sol 和 TestStoryDao.js。由於 Truffle 中沒有本地方法來測試異常,因此還使用以下內容創建 helpers/expectThrow.js:
export default async promise => { try { await promise; } catch (error) { const invalidOpcode = error.message.search('invalid opcode') >= 0; const outOfGas = error.message.search('out of gas') >= 0; const revert = error.message.search('revert') >= 0; assert( invalidOpcode || outOfGas || revert, 'Expected throw, got \'' + error + '\' instead', ); return; } assert.fail('Expected throw not received'); };
注意:Solidity 測試通常用於測試低級、基於合約的函數,即智能合約的內部結構。 JS 測試通常用於測試合約是否可以從外部正確交互,這是我們的最終用戶將要執行的操作。
在 TestStoryDao.sol 中,放入以下內容:
pragma solidity ^0.4.24; import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol"; import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol"; contract StoryDao is Ownable { using SafeMath for uint256; mapping(address => bool) whitelist; uint256 public whitelistedNumber = 0; mapping(address => bool) blacklist; event Whitelisted(address addr, bool status); event Blacklisted(address addr, bool status); uint256 public daofee = 100; // 百分之几,即 100 为 1% uint256 public whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币 event SubmissionCommissionChanged(uint256 newFee); event WhitelistFeeChanged(uint256 newFee); uint256 public durationDays = 21; // 故事章节持续时间(天) uint256 public durationSubmissions = 1000; // 故事章节持续时间(条目) function changedaofee(uint256 _fee) onlyOwner external { require(_fee <= 1000); // 限制最大费用为 10% daofee = _fee; emit SubmissionCommissionChanged(_fee); } function changewhitelistfee(uint256 _fee) onlyOwner external { require(_fee > 0); // 确保费用大于 0 whitelistfee = _fee; emit WhitelistFeeChanged(_fee); } function changeDurationDays(uint256 _days) onlyOwner external { require(_days >= 1); durationDays = _days; } function changeDurationSubmissions(uint256 _subs) onlyOwner external { require(_subs > 99); durationSubmissions = _subs; } }
這將檢查 StoryDao 合約是否以正確的費用和持續時間數字正確部署。第一行確保它通過從已部署地址列表中讀取它來部署,並且最後一節進行一些斷言——檢查聲明是真還是假。在我們的例子中,我們將數字與已部署合約的初始值進行比較。每當它是“真”時,Assert.equals 部分都會發出一個事件,說明“真”,這就是 Truffle 在測試時正在監聽的。
在 TestStoryDao.js 中,放入以下內容:
modifier onlyOwner() { require(msg.sender == owner); _; }
為了使我們的測試能夠成功運行,我們還需要告訴 Truffle 我們希望部署 StoryDao——因為它不會為我們這樣做。因此,讓我們在遷移中使用幾乎與我們之前編寫的遷移相同的 content 創建 3_deploy_storydao.js:
export default async promise => { try { await promise; } catch (error) { const invalidOpcode = error.message.search('invalid opcode') >= 0; const outOfGas = error.message.search('out of gas') >= 0; const revert = error.message.search('revert') >= 0; assert( invalidOpcode || outOfGas || revert, 'Expected throw, got \'' + error + '\' instead', ); return; } assert.fail('Expected throw not received'); };
此時,我們還應該更新(或創建,如果不存在)項目文件夾根目錄中的package.json 文件,其中包含我們到目前為止需要的依賴項,以及將來可能需要的依賴項:
pragma solidity ^0.4.24; import "truffle/Assert.sol"; import "truffle/DeployedAddresses.sol"; import "../contracts/StoryDao.sol"; contract TestStoryDao { function testDeploymentIsFine() public { StoryDao sd = StoryDao(DeployedAddresses.StoryDao()); uint256 daofee = 100; // 百分之几,即 100 为 1% uint256 whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币 uint256 durationDays = 21; // 故事章节持续时间(天) uint256 durationSubmissions = 1000; // 故事章节持续时间(条目) Assert.equal(sd.daofee(), daofee, "初始 DAO 费用应为 100"); Assert.equal(sd.whitelistfee(), whitelistfee, "初始白名单费用应为 0.01 以太币"); Assert.equal(sd.durationDays(), durationDays, "初始天数持续时间应设置为 3 周"); Assert.equal(sd.durationSubmissions(), durationSubmissions, "初始提交持续时间应设置为 1000 个条目"); } }
以及包含以下內容的 .babelrc 文件:
import expectThrow from './helpers/expectThrow'; const StoryDao = artifacts.require("StoryDao"); contract('StoryDao Test', async (accounts) => { it("should make sure environment is OK by checking that the first 3 accounts have over 20 eth", async () =>{ assert.equal(web3.eth.getBalance(accounts[0]).toNumber() > 2e+19, true, "Account 0 has more than 20 eth"); assert.equal(web3.eth.getBalance(accounts[1]).toNumber() > 2e+19, true, "Account 1 has more than 20 eth"); assert.equal(web3.eth.getBalance(accounts[2]).toNumber() > 2e+19, true, "Account 2 has more than 20 eth"); }); it("should make the deployer the owner", async () => { let instance = await StoryDao.deployed(); assert.equal(await instance.owner(), accounts[0]); }); it("should let owner change fee and duration", async () => { let instance = await StoryDao.deployed(); let newDaoFee = 50; let newWhitelistFee = 1e+10; // 1 ether let newDayDuration = 42; let newSubsDuration = 1500; instance.changedaofee(newDaoFee, {from: accounts[0]}); instance.changewhitelistfee(newWhitelistFee, {from: accounts[0]}); instance.changeDurationDays(newDayDuration, {from: accounts[0]}); instance.changeDurationSubmissions(newSubsDuration, {from: accounts[0]}); assert.equal(await instance.daofee(), newDaoFee); assert.equal(await instance.whitelistfee(), newWhitelistFee); assert.equal(await instance.durationDays(), newDayDuration); assert.equal(await instance.durationSubmissions(), newSubsDuration); }); it("should forbid non-owners from changing fee and duration", async () => { let instance = await StoryDao.deployed(); let newDaoFee = 50; let newWhitelistFee = 1e+10; // 1 ether let newDayDuration = 42; let newSubsDuration = 1500; await expectThrow(instance.changedaofee(newDaoFee, {from: accounts[1]})); await expectThrow(instance.changewhitelistfee(newWhitelistFee, {from: accounts[1]})); await expectThrow(instance.changeDurationDays(newDayDuration, {from: accounts[1]})); await expectThrow(instance.changeDurationSubmissions(newSubsDuration, {from: accounts[1]})); }); it("should make sure the owner can only change fees and duration to valid values", async () =>{ let instance = await StoryDao.deployed(); let invalidDaoFee = 20000; let invalidDayDuration = 0; let invalidSubsDuration = 98; await expectThrow(instance.changedaofee(invalidDaoFee, {from: accounts[0]})); await expectThrow(instance.changeDurationDays(invalidDayDuration, {from: accounts[0]})); await expectThrow(instance.changeDurationSubmissions(invalidSubsDuration, {from: accounts[0]})); }) });
我們還需要在 Truffle 配置中要求 Babel,以便它知道在編譯測試時應該使用它。
注意:Babel 是 NodeJS 的一個附加組件,它允許我們在當前一代 NodeJS 中使用下一代 JavaScript,因此我們可以編寫 import 等內容。如果您不理解這一點,只需忽略它並逐字粘貼即可。安裝後,您可能再也不用處理這個問題了。
var Migrations = artifacts.require("./Migrations.sol"); var StoryDao = artifacts.require("./StoryDao.sol"); module.exports = function(deployer, network, accounts) { if (network == "development") { deployer.deploy(StoryDao, {from: accounts[0]}); } else { deployer.deploy(StoryDao); } };
現在,最後運行 truffle test。輸出應該類似於此:
有關測試的更多信息,請參閱本教程,我們專門準備了本教程來涵蓋智能合約的測試。
在本課程的後續部分中,我們將跳過測試,因為逐字輸入它們會使教程過長,但請參考項目的最終源代碼以檢查所有測試。我們剛剛完成的過程已經為測試設置了環境,因此您可以編寫測試而無需進一步設置。
白名單
現在讓我們構建白名單機制,它允許用戶參與構建故事。將以下函數框架添加到 StoryDao.sol:
{ "name": "storydao", "devDependencies": { "babel-preset-es2015": "^6.18.0", "babel-preset-stage-2": "^6.24.1", "babel-preset-stage-3": "^6.17.0", "babel-polyfill": "^6.26.0", "babel-register": "^6.23.0", "dotenv": "^6.0.0", "truffle": "^4.1.12", "openzeppelin-solidity": "^1.10.0", "openzeppelin-solidity-metadata": "^1.2.0", "openzeppelin-zos": "", "truffle-wallet-provider": "^0.0.5", "ethereumjs-wallet": "^0.6.0", "web3": "^1.0.0-beta.34", "truffle-assertions": "^0.3.1" } }
未命名的函數 function() 被稱為回退函數,當向此合約發送資金但沒有特定指令(即沒有專門調用另一個函數)時,就會調用此函數。這允許人們通過僅向 DAO 發送以太幣來加入 StoryDao,並根據他們是否已列入白名單來立即列入白名單或購買代幣。
whitelistSender 函數用於列入白名單,可以直接調用,但我們將確保回退函數在收到一些以太幣後自動調用它,前提是發送者尚未列入白名單。 whitelistAddress 函數被聲明為 public,因為它也應該可以從其他合約調用,而回退函數是 external,因為資金只會從外部地址發送到此地址。調用此合約的合約可以輕鬆直接調用所需函數。
讓我們首先處理回退函數。
pragma solidity ^0.4.24; import "../node_modules/openzeppelin-solidity/contracts/math/SafeMath.sol"; import "../node_modules/openzeppelin-solidity/contracts/ownership/Ownable.sol"; contract StoryDao is Ownable { using SafeMath for uint256; mapping(address => bool) whitelist; uint256 public whitelistedNumber = 0; mapping(address => bool) blacklist; event Whitelisted(address addr, bool status); event Blacklisted(address addr, bool status); uint256 public daofee = 100; // 百分之几,即 100 为 1% uint256 public whitelistfee = 10000000000000000; // 以 Wei 为单位,这是 0.01 以太币 event SubmissionCommissionChanged(uint256 newFee); event WhitelistFeeChanged(uint256 newFee); uint256 public durationDays = 21; // 故事章节持续时间(天) uint256 public durationSubmissions = 1000; // 故事章节持续时间(条目) function changedaofee(uint256 _fee) onlyOwner external { require(_fee <= 1000); // 限制最大费用为 10% daofee = _fee; emit SubmissionCommissionChanged(_fee); } function changewhitelistfee(uint256 _fee) onlyOwner external { require(_fee > 0); // 确保费用大于 0 whitelistfee = _fee; emit WhitelistFeeChanged(_fee); } function changeDurationDays(uint256 _days) onlyOwner external { require(_days >= 1); durationDays = _days; } function changeDurationSubmissions(uint256 _subs) onlyOwner external { require(_subs > 99); durationSubmissions = _subs; } }
我們檢查發送者是否尚未列入白名單,並將調用委託給 whitelistAddress 函數。請注意,我們註釋掉了 buyTokens 函數,因為我們還沒有它。
接下來,讓我們處理白名單。
modifier onlyOwner() { require(msg.sender == owner); _; }
請注意,此函數接受地址作為參數,而不是從消息(從事務)中提取它。這還有一個額外的好處,即如果有人負擔不起加入 DAO 的費用,例如,人們可以將其他人列入白名單。
我們從一些健全性檢查開始函數:發送者不得已列入白名單或列入黑名單(被禁止),並且必須已發送足夠的費用來支付費用。如果這些條件令人滿意,則將地址添加到白名單,發出 Whitelisted 事件,最後,如果發送的以太幣數量大於支付白名單費用所需的以太幣數量,則剩餘部分將用於購買代幣。
注意:我們使用 sub 而不是 - 來減去,因為這是用於安全計算的 SafeMath 函數。
只要用戶向 StoryDao 合約發送 0.01 以太幣或更多以太幣,他們現在就可以將自己或其他人列入白名單。
結論
在本教程中,我們構建了 DAO 的初始部分,但還有很多工作要做。敬請期待:在下一部分中,我們將處理向故事中添加內容!
有關構建以太坊 DApp 和白名單的常見問題解答
構建以太坊 DApp 的先決條件是什麼?
在開始構建以太坊 DApp 之前,您需要對區塊鏈技術、以太坊和智能合約有基本的了解。您還應該熟悉 JavaScript 和 Solidity 等編程語言,Solidity 用於編寫以太坊上的智能合約。此外,您需要安裝 Node.js、Truffle、Ganache 和 MetaMask 等工具,這些工具對於開發和測試 DApp 至關重要。
DApp 中的白名單流程如何運作?
白名單是 DApp 中用於限制對應用程序的某些功能或區域的訪問的安全措施。它涉及創建一個批准的地址列表,這些地址允許與 DApp 交互。只有從這些地址發起的交易才會被接受,而其他交易將被拒絕。這有助於防止未經授權的訪問和惡意活動。
智能合約在 DApp 中的作用是什麼?
智能合約是自執行合約,其協議條款直接寫入代碼中。它們在 DApp 中發揮著至關重要的作用,因為它們可以自動化區塊鏈上業務邏輯的執行。它們確保透明度、安全性和不可變性,因為一旦部署,它們就無法更改或篡改。
如何測試我的 DApp?
測試是 DApp 開發中至關重要的一部分,用於確保其功能和安全性。您可以使用 Truffle 和 Ganache 等工具進行測試。 Truffle 為以太坊提供開發環境、測試框架和資產管道,而 Ganache 允許您創建用於測試的私有以太坊區塊鏈。
什麼是 DAO,它與 DApp 有何關係?
DAO 代表去中心化自治組織。它是一種由編碼為計算機程序的規則表示的組織類型,該程序是透明的,由組織成員控制,不受中央政府的影響。 DAO 的財務交易和規則保存在區塊鏈上,這使其成為一種 DApp。
如何確保我的 DApp 的安全性?
確保 DApp 的安全性涉及多種實踐。這包括編寫安全的智能合約、徹底測試、進行安全審計以及保持軟件和依賴項的最新狀態。遵循安全編碼的最佳實踐並隨時了解區塊鏈領域最新的安全漏洞和威脅也很重要。
什麼是 MetaMask,它在 DApp 開發中為什麼很重要?
MetaMask 是一個瀏覽器擴展,允許您直接從瀏覽器與以太坊區塊鍊和 DApp 交互。它還可以作為以太坊錢包來管理您的以太幣和 ERC-20 代幣。它在 DApp 開發中很重要,因為它為用戶提供了一個用戶友好的界面,以便用戶無需運行完整的以太坊節點即可與您的 DApp 交互。
如何部署我的 DApp?
一旦您開發並測試了您的 DApp,您就可以將其部署到以太坊主網或測試網上。這涉及編譯您的智能合約,將它們部署到區塊鏈,並將您的 DApp 連接到這些合約。您可以使用 Truffle 和 Infura 等工具來完成此過程。
DApp 開發的挑戰是什麼?
DApp 開發麵臨著一些挑戰。這包括處理以太坊網絡的可擴展性問題、確保 DApp 的安全性、管理交易的波動性燃氣價格以及提供用戶友好的界面。它還需要隨時了解快速發展的區塊鏈技術和法規。
部署後如何更新我的 DApp?
部署後更新 DApp 可能具有挑戰性,因為區塊鏈上的智能合約是不可變的。但是,您可以通過將數據和邏輯分離到不同的合約中或使用委託調用來升級合約來設計可升級的合約。在 DApp 的設計階段規劃升級和更改非常重要。
以上是建造以太坊dapps:白名單和測試一個故事dao的詳細內容。更多資訊請關注PHP中文網其他相關文章!

中国女性科技力量在AI领域的崛起:荣耀与DeepSeek合作背后的女性故事女性在科技领域的贡献日益显著。中国科技部数据显示,女性科技工作者数量庞大,在AI算法开发中展现出独特的社会价值敏感性。本文将聚焦荣耀手机,探究其率先接入DeepSeek大模型背后的女性团队力量,展现她们如何推动科技进步,重塑科技发展价值坐标系。2024年2月8日,荣耀正式上线DeepSeek-R1满血版大模型,成为安卓阵营首家接入DeepSeek的厂商,引发用户热烈反响。这一成功背后,女性团队成员在产品决策、技术攻坚和用户

DeepSeek公司在知乎發布技術文章,詳細介紹了其DeepSeek-V3/R1推理系統,並首次公開關鍵財務數據,引發業界關注。文章顯示,該系統單日成本利潤率高達545%,創下全球AI大模型盈利新高。 DeepSeek的低成本策略使其在市場競爭中佔據優勢。其模型訓練成本僅為同類產品的1%-5%,V3模型訓練成本僅為557.6萬美元,遠低於競爭對手。同時,R1的API定價僅為OpenAIo3-mini的1/7至1/2。這些數據證明了DeepSeek技術路線的商業可行性,也為AI大模型的高效盈利樹立了

網站建設只是第一步:SEO與反向鏈接的重要性 建立網站只是將其轉化為寶貴營銷資產的第一步。您需要進行SEO優化,以提高網站在搜索引擎中的可見度,吸引潛在客戶。反向鏈接是提升網站排名的關鍵,它向谷歌和其他搜索引擎表明您的網站權威性和可信度。 並非所有反向鏈接都有利:識別並避免有害鏈接 並非所有反向鏈接都有益。有害鏈接會損害您的排名。優秀的免費反向鏈接檢查工具可以監控鏈接到您網站的來源,並提醒您注意有害鏈接。此外,您還可以分析競爭對手的鏈接策略,從中學習借鑒。 免費反向鏈接檢查工具:您的SEO情報員

美的即将发布搭载DeepSeek大模型的首款空调——美的鲜净感空气机T6,发布会定于3月1日下午1点30分举行。这款空调配备先进的空气智驾系统,可根据环境智能调节温度、湿度和风速等参数。更重要的是,它集成了DeepSeek大模型,支持超过40万条AI语音指令。美的此举引发业界热议,尤其关注白电产品与大模型结合的意义。不同于传统空调简单的温度设定,美的鲜净感空气机T6能够理解更复杂、更模糊的指令,并根据家庭环境智能调节湿度等,显著提升用户体验。

DeepSeek-R1賦能百度文庫與網盤:深度思考與行動的完美融合短短一個月內,DeepSeek-R1已迅速融入眾多平台。百度憑藉大膽的戰略佈局,將DeepSeek作為第三方模型夥伴,整合進自身生態系統,這標誌著其“大模型 搜索”生態戰略的重大進展。百度搜索和文心智能體平台率先接入DeepSeek及文心大模型的深度搜索功能,為用戶提供免費的AI搜索體驗。同時,“百度一下,你就知道”的經典slogan回歸,新版百度APP也整合了文心大模型和DeepSeek的能力,推出“AI搜索”、“全網信息提煉”

此基於GO的網絡漏洞掃描儀有效地確定了潛在的安全弱點。 它利用了GO的並發功能的速度功能,包括服務檢測和漏洞匹配。讓我們探索它的能力和道德

AI及時的代碼生成工程:開發人員指南代碼開發的景觀已準備好進行重大轉變。 掌握大型語言模型(LLM)和及時工程對於未來幾年對開發人員至關重要。 Th


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SublimeText3漢化版
中文版,非常好用

Dreamweaver Mac版
視覺化網頁開發工具

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。