نحوه استفاده از Dapptools

Dapptools

استفاده از Dapptools

در این مقاله تلاش می شود با نحوه استفاده از Dapptools، که چارچوبی برای استقرار قرارداد هوشمند برای توسعه دهندگان وب 3 که عاشق bash و خط فرمان هستند، آشنا شوید. MakerDAO یکی از بزرگ‌ ترین پروتکل‌ های DeFi است و استیبل کوین “DAI” یکی از پرکاربرد ترین پروتکل‌ ها در این صنعت است. تیم آن ها از یک چارچوب ویژه به نام Dapptools برای ایجاد، استقرار، آزمایش (تست و ارزیابی) و تعامل با قراردادهای هوشمند خود استفاده می کند.

فریمورک Dapptools که توسط تیم Dapphub ایجاد شده است، یک ابزار مینیمالیستی bash-friendly است که هر کاربر قدرتمند لینوکس به راحتی عاشق آن خواهد شد و بسیاری از آن استفاده خواهند کرد. همچنین، این فریمورک بسیار مبتدی پسند است، بنابراین اگر این اولین نگاه شما به چارچوب استقرار است، به جای درستی آمده اید. در این مقاله قصد داریم نحوه انجام کارهای زیر را با Dapptools نشان دهیم:

  1. قراردادها را بنویسید و کامپایل کنید.
  2. انجام تست و ارزیابی قراردادها
  3. قراردادها را مستقر کنید.
  4. تعامل با قراردادهای مستقر

قصد داریم از Dapptools-demo استفاده کنیم تا در مورد آن نیز بیاموزیم. اگر بخواهید، می توانید ابزار Foundry را نیز بررسی کنید، که بازنویسی از Dapptools است، اما توسط تیم Paradigm انجام شده است. برای یک مخزن کامل تر با کدها و مثال های خوب بیشتر، کیت Dapptools-starter را بررسی کنید، این کیت شامل نمونه های کد با استفاده از Chainlink است!

برپایی محیط

ابتدا شما به یک ویرایشگر کد نیاز دارید (VSCode). اگر از ویندوز استفاده می کنید، باید WSL را دانلود کنید؛ زیرا تعدادی از دستورات را در ویندوز اجرا خواهیم کرد. هنگامی که از VSCode استفاده می کنید، یک ترمینال برای اجرای دستورات برای نصب یا هر روشی که معمولاً دستورات پوسته را اجرا می کنید، باز کنید.

نصب / الزامات

  1. Git
  2. Make
  3. Dapptools

ابتدا باید git را نصب کنید. به کمک دستور زیر می توانید از نصب صحیح آن مطلع شوید.

git --version

سپس، Dapptools را نصب کنید. حتماً برای نصب به اسناد رسمی بروید، نصب آن شامل مراحل زیر خواهد بود:

# user must be in sudoers

curl -L https://nixos.org/nix/install | sh




# Run this or login again to use Nix

. "$HOME/.nix-profile/etc/profile.d/nix.sh"




curl https://dapp.tools/install | sh

شما باید dapp ،seth ،ethsign ،hevm و چند دستور دیگر را نیز داشته باشید.

این دستورالعمل‌ها فقط برای سیستم‌ های مبتنی بر یونیکس کار می‌کنند (به عنوان مثال، MacOS و ..)

یک پروژه Dapptools محلی ایجاد کنید. برای ایجاد یک پوشه جدید، دستور زیر را اجرا کنید:

dapp init

با اجرای این دستور، پوشه هایی به قرار زیر برای شما ایجاد خواهد شد:

.
├── Makefile
├── lib
│   └── ds-test
│       ├── LICENSE
│       ├── Makefile
│       ├── default.nix
│       ├── demo
│       │   └── demo.sol
│       └── src
│           └── test.sol
├── out
│   └── dapp.sol.json
└── src
├── DapptoolsDemo.sol
└── DapptoolsDemo.t.sol

  1. Makefile: جایی که “اسکریپت‌ های” خود را قرار می‌دهید. Dapptools مبتنی بر خط فرمان است و فایل Makefile به ما کمک می کند دستورات بزرگ را با چند کاراکتر اجرا کنیم.
  2. lib: این پوشه برای وابستگی های خارجی مانند Openzeppelin یا ds-test است.
  3. out: جایی که کد کامپایل شده شما می رود. مشابه پوشه build در brownie یا پوشه artifacts در hardhat.
  4. src: قراردادهای هوشمند شما اینجاست. مشابه پوشه contracts در brownie و hardhat.

احرای تست ها

برای اجرای تست ها، فقط باید دستور زیر را اجرا کنید:

dapp test

و خروجی مانند زیر خواهد بود:

Running 2 tests for src/DapptoolsDemo.t.sol:DapptoolsDemoTest

[PASS] test_basic_sanity() (gas: 190)

[PASS] testFail_basic_sanity() (gas: 2355)

Fuzzing

Dapptools به صورت داخلی با تاکید بر fuzzing ارائه می شود. ابزاری فوق العاده قدرتمند برای آزمایش قراردادهای ما با داده های تصادفی.

DapptoolsDemo.sol خود را با تابعی به نام play به روز رسانی کنید. در اینجا فایل قرارداد جدید باید به صورت زیر باشد:

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.6;

contract DapptoolsDemo {

function play(uint8 password) public pure returns(bool){

        if(password == 55){

            return false;

        }

        return true;

    }

}

همچنین؛ یک تست جدید به نام test_basic_fuzzing در DappToolsDemo.t.sol خود اضافه خواهیم کرد، که به صورت زیر است:

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.6;

import "ds-test/test.sol";

import "./DapptoolsDemo.sol";

contract DapptoolsDemoTest is DSTest {

    DapptoolsDemo demo;

function setUp() public {

        demo = new DapptoolsDemo();

    }




function testFail_basic_sanity() public {

        assertTrue(false);

    }




function test_basic_sanity() public {

        assertTrue(true);

    }




function test_basic_fuzzing(uint8 value) public {

        bool response = demo.play(value);

        assertTrue(response);

    }

}

اکنون می‌ توانیم داده‌ های تصادفی قراردادمان را به عنوان ورودی اعمال کنیم، و اگر کد ما عدد 55 را به آن بدهد، انتظار داریم با خطا مواجه شود. آزمایش‌ های خود را اکنون با پرچم fuzzing اجرا می کنیم:

dapp test — fuzz-runs 1000

و خروجی شبیه به زیر را خواهیم دید:

+ dapp clean

+ rm -rf out

Running 3 tests for src/DapptoolsDemo.t.sol:DapptoolsDemoTest

[PASS] test_basic_sanity() (gas: 190)

[PASS] testFail_basic_sanity() (gas: 2355)

[FAIL] test_basic_fuzzing(uint8). Counterexample: (55)

Run:

dapp test --replay '("test_basic_fuzzing(uint8)","0x0000000000000000000000000000000000000000000000000000000000000037")'

to test this case again, or

dapp debug --replay

'("test_basic_fuzzing(uint8)","0x0000000000000000000000000000000000000

000000000000000000000000037")'

to debug it.




Failure:




  Error: Assertion Failed

آزمایش‌ های فازی ما موارد خطا و خارج از دامنه را مشخص کردند! 1000 مسیر مختلف را برای تست test_basic_fuzzing انجام داده ایم که در مرحله 55 به پاسخ رسیدیم. این امر برای یافتن موارد استفاده تصادفی که قراردادهای شما را شکسته است، بسیار مهم است.

import از Openzeppelin و قراردادهای خارجی

فرض کنید می خواهیم یک NFT با استفاده از استاندارد Openzeppelin ایجاد کنیم. برای نصب قراردادها یا بسته های خارجی، می توانیم از دستور نصب dapp استفاده کنیم. برای نصب باید نام سازمان مخزن GitHub و نام مخزن را نامگذاری کنیم.

ابتدا باید تغییرات خود را تا اینجای کار انجام دهیم. Dapptools بسته های خارجی را به عنوان زیر ماژول های git وارد می کند (import)، بنابراین ابتدا باید آن را commit کنیم.

Run

git add .

git commit -m ‘initial commit’

سپس، می توانیم بسته های خارجی خود را نصب کنیم. به عنوان مثال، برای OpenZeppelin، ما از دستور زیر استفاده می کنیم:

dapp install OpenZeppelin/openzeppelin-contracts

شما باید یک پوشه جدید در پوشه lib خود ببینید که اکنون با عنوان openzeppelin-contracts نامگذاری شده است.

قرارداد NFT

یک فایل جدید در پوشه src به نام NFT.sol ایجاد کنید و کد زیر را در آن قرار دهید:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract NFT is ERC721 {

    uint256 public tokenCounter;

    constructor () ERC721 ("NFT", "NFT"){

        tokenCounter = 0;

    }

function createCollectible() public returns (uint256) {

        uint256 newItemId = tokenCounter;

        _safeMint(msg.sender, newItemId);

        tokenCounter = tokenCounter + 1;

        return newItemId;

    }




}

اگر اکنون سعی کنید ساخت Dapp را انجام دهید، یک خطای بزرگ دریافت خواهید کرد!

Mapping مجدد

ما باید به Dapptools بگوییم که “@openzeppelin/contracts/token/ERC721/ERC721.sol” را که به پوشه lib ما اشاره دارد، import کند. بنابراین ما یک فایل به نام “remappings.txt” می سازیم و دستور زیر را در آن اجرا می کنیم:

@openzeppelin/=lib/openzeppelin-contracts/

ds-test/=lib/ds-test/src/

سپس یک فایل به نام .dapprc می سازیم ودستور زیر را در آن می نویسیم:

export DAPP_REMAPPINGS=$(cat remappings.txt)

Dapptools به .dapprc ایجاد شده برای متغیرهای پیکربندی شدۀ مختلف نگاه می کند، به نوعی مانند hardhat.config.js در hardhat است. در این فایل پیکربندی، به آن می‌گوییم که خروجی remappings.txt را بخواند و از آن‌ ها به عنوان «remappings» استفاده کند. نگاشت مجدد روشی است که به import کردن خود می گوییم که فایل ها را از کجا باید وارد کنیم. به عنوان مثال در remapping.txt ما می بینیم:

@openzeppelin/=lib/openzeppelin-contracts/

این بدان معناست که ما به dapptools می گوییم که وقتی یک فایل را کامپایل می کند و @openzeppelin/ را در یک دستور import می بیند، باید به دنبال فایل ها در lib/openzeppelin-contracts/ باشد. بنابراین با اجرای دستور زیر:

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

منظور ما کد زیر است:

import "lib/openzeppelin-contracts/contracts/token/ERC721/ERC721.sol";

سپس، برای اینکه کل کتابخانه را کامپایل نکنیم، باید موارد زیر را به فایل «.dapprc» خود اضافه کنیم:

export DAPP_LINK_TEST_LIBRARIES=0

این کد به dapptools می‌گوید که هنگام اجرای آزمایش‌ ها، همه چیز را در lib کامپایل نکند.

استقرار در یک شبکه آزمایشی (در صورت تمایل شبکه اصلی)

نکته: اگر می خواهید شبکه محلی خود را راه اندازی کنید، می توانید dapp testnet را اجرا کنید.

.env را به فایل .gitignore خود اضافه کنید.

اگر از قبل آن را ندارید، یک فایل .gitignore ایجاد کنید و فقط این خط را داخل آن اضافه کنید:

.env

از انجام این کار هراسی نداشته باشید، با این روش به هیچ وجه کلید خصوصی شما برای git ارسال نمی شود، اما می‌ خواهیم عادت کنیم همیشه آن را به .gitignore خود اضافه کنیم! این کار به شما در برابر ارسال تصادفی متغیرهای محیطی به مخزن عمومی git کمک می کند.

یک متغیر محیطی ETH_RPC_URL را تنظیم کنید

برای استقرار در یک شبکه آزمایشی، به یک گره بلاکچین نیاز داریم. یک انتخاب فوق العاده پروژه کیمیاگری است. شما می توانید یک نقطه پایانی HTTP تست شبکه رایگان دریافت کنید. فقط برای یک پروژه رایگان ثبت نام کنید، و کلید view را بزنید (یا هر متنی که در آن زمان وجود دارد) و یک نقطه پایانی HTTP خواهید داشت!

اگر می خواهید در کار با شبکه های آزمایشی مختلف مهارت داشته باشید، می توانید شبکه آزمایشی مورد نظر خود را انتخاب کنید. ما یکی از shareهای زنجیره‌ ای را انتخاب می‌ کنیم که می‌ توانید هم LINK testnet و هم ETH را دریافت کنید. Kovan یا Rinkeby انتخاب های خوبی خواهند بود، بنابراین با هر کدام  که خواستید می توانید کار کنید.

اگر قبلاً این کار را نکرده‌ اید، یک فایل .env ایجاد کنید، سپس نقطه پایانی خود را به فایل .env خود اضافه کنید، که به صورت زیر خواهد بود:

export ETH_RPC_URL=http://alchemy.io/adfsasdfasdf

یک فرستنده پیش فرض ایجاد کنید

اگر قبلاً این کار را نکرده اید، یک کیف پول et بگیرید. در حالت ایده‌ آل، شما یک متامسک را نصب می کنید و سپس مقداری ETH شبکه آزمایشی را از Chainlink Faucets دریافت می‌ کنید. سپس به شبکه آزمایشی که با آن کار می کنید بروید. متاماسک شما باید چیزی شبیه به این باشد:

هنگامی که یک کیف پول دارید، آدرس آن کیف پول را به عنوان یک متغیر محیطی ETH_FROM تنظیم کنید.

export ETH_FROM=YOUR_ETH_WALLET_ADDRESS

علاوه بر این، در صورت استفاده از Kovan، کیف پول خود را با testnet ETH  شارژ کنید.

کلید خصوصی خود را اضافه کنید

نکته: از متامسکی استفاده کنید که پول واقعی در آن برای توسعه وجود ندارد.

اگر کلید خصوصی خود را به یک مخزن عمومی با پول واقعی اعمال کنید، مردم می توانند وجوه شما را بدزدند.

Dapptools با ابزاری به نام ethsign عرضه می‌ شود، و اینجا جایی است که می‌ خواهیم کلید خود را ذخیره و رمز گذاری کنیم. برای افزودن کلید خصوصی (برای ارسال تراکنش‌ ها لازم است) کلید خصوصی کیف پول خود را دریافت و اجرا کنید:

ethsign import

سپس از شما می خواهد که کلید خصوصی خود را اضافه کنید و رمز عبوری برای رمز گذاری آن انتخاب کنید. این کار کلید خصوصی شما را در ethsign رمز گذاری می کند. هر زمان که بخواهید تراکنش را به جلو ارسال کنید، به رمز عبور خود نیاز خواهید داشت. اگر دستور ethsign ls را اجرا کنید، پاسخی مانند زیر دریافت می کنید:

0x3DF02ac6fEe39B79654AA81C6573732439e73A81 keystore

Makefile خود را به روز رسانی کنید

دستوری که می توانیم برای استقرار قراردادهای خود استفاده کنیم، dapp create DapptoolsDemo است و سپس چند پرچم برای اضافه کردن متغیرهای محیطی ایجاد می شود. برای آسان‌ تر کردن فرایند، می‌ توانیم دستور deploy خود را به Makefile اضافه کنیم و فقط به Makefile بگوییم که از متغیرهای محیطی ما استفاده کند.

موارد زیر را به Makefile خود اضافه کنید:

-include .env

استقرار قرارداد

در Makefile ما دستوری به نام deploy داریم، دستور dapp create DapptoolsDemo را اجرا می‌ کند و متغیرهای محیطی ما را شامل می‌ شود. برای اجرای آن، فقط دستور زیر را اجرا کنید:

make deploy

رمز عبور از شما خواسته می شود. پس از موفقیت، قرارداد شما را مستقر می کند!

dapp create DapptoolsDemo

++ seth send --create

608060405234801561001057600080fd5b50610158806100206000396000f3

fe608060405234801561001057600080fd5b506004361061002b5760003560

e01c806353a04b0514610030575b600080fd5b61004a600480360381019061

00459190610096565b610060565b60405161005791906100d2565b60405180

910390f35b600060378260ff161415610077576000905061007c565b600190

505b919050565b6000813590506100908161010b565b92915050565b600060

2082840312156100ac576100ab610106565b5b60006100ba84828501610081

565b91505092915050565b6100cc816100ed565b82525050565b6000602082

0190506100e760008301846100c3565b92915050565b600081151590509190

50565b600060ff82169050919050565b600080fd5b610114816100f9565b81

1461011f57600080fd5b5056fea264697066735822122004d7143940853a76

50f1383002b6ba56991e7a5c7d763e755774a149ca0465e364736f6c634300

08060033 'DapptoolsDemo()'

seth-send: warning: `ETH_GAS' not set; using default gas

amount

Ethereum account passphrase (not echoed): seth-send: Published

transaction with 376 bytes of calldata.

seth-send:

0xeb871eee1fa31c34583b63002e2b16a0252410b5615623fd254b1f90b673

69d4

seth-send: Waiting for transaction receipt........

seth-send: Transaction included in block 29253678.

0xC5a62934B912c3B1948Ab0f309e31a9b8Ed08dd1

شما باید بتوانید آدرس نهایی داده شده در Etherscan را ببینید.

تعامل با قراردادها

برای تعامل با قراردادهای مستقر شده، می‌ توانیم از seth call و seth send استفاده کنیم. آن ها با یکدیگر تفاوت های اندکی دارند:

  1. seth call: فقط داده ها را از بلاکچین می خواند. هیچ gas خرج نمی کند.
  2. seth send: این کار یک تراکنش به بلاکچین ارسال می کند، به صورت بالقوه وضعیت بلاکچین را تغییر می دهد و gas مصرف می کند.

برای “خواندن” داده ها از بلاکچین، می توانیم کاری مانند زیر را انجام دهیم:

ETH_RPC_URL=<YOUR_RPC_URL> seth call <YOUR_DEPLOYED_CONTRACT>

"FUNCTION_NAME()" <ARGUMENTS_SEPARATED_BY_SPACE>

و یا دستوری مانند زیر:

ETH_RPC_URL=<YOUR_RPC_URL> seth call 0x12345 "play(uint8)" 55

که خروجی شما به صورت زیر خواهد بود:

0x0000000000000000000000000000000000000000000000000000000000000000`

که به معنای نادرست است، زیرا آن پاسخ برابر با 0 است، و در یک متغیر بولین، 0 به معنای نادرست است.

برای “نوشتن” داده ها در بلاکچین، می توانیم دستوری شبیه به کد زیر را اجرا کنیم:

ETH_RPC_URL=<YOUR_RPC_URL> ETH_FROM=<YOUR_FROM_ADDRESS> seth

send <YOUR_DEPLOYED_CONTRACT> "FUNCTION_NAME()"

<ARGUMENTS_SEPARATED_BY_SPACE>

ما قراردادی را مستقر نکرده‌ایم که نمونه‌ ای عالی برای انجام این کار داشته باشد، اما فرض کنید که تابع play می‌ تواند وضعیت بلاکچین را تغییر دهد، که به صورت زیر است:

ETH_RPC_URL=<YOUR_RPC_URL> ETH_FROM=<YOUR_FROM_ADDRESS> seth

send 0x12345 "play(uint8)" 55

قرارداد خود را در Etherscan تأیید کنید. بعد از اینکه قراردادی را برای اسکن اتر انجام دادید، می‌ توانید آن را از طریق API Etherscan دریافت کنید که در حال اجرا به صورت زیر است:

ETHERSCAN_API_KEY=<api-key> dapp verify-contract

<contract_directory>/<contract>:<contract_name>

<contract_address>

به عنوان مثال:

ETHERSCAN_API_KEY=123456765 dapp verify-contract

./src/DapptoolsDemo.sol:DapptoolsDemo 0x23456534212536435424

در نهایت…

  1. کش را به .gitignore خود اضافه کنید.
  2. اضافه کردن به روز رسانی: به روز رسانی dapp در بالای Makefile شما. این کار فایل‌ های موجود در .gitmodules و lib را با اجرای make به‌ روز رسانی و دانلود می‌ کند.
  3. یک مجوز اضافه کنید.
محمد امیدی
ارسال دیدگاه

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *