目前支流的合约开发次要有 ETH 的 solidity,Solana 的 bpf(linux 中罕用)以及 wasm; 对于这些合约的开发,每条公链接都有本人的工具和框架,升高开发者在应用中的难度;明天就挑了三个比照,他们别离是 ETH 的 hardhat 工具,Solana 的 anchor 和 substrate 的 ink!。
你将理解如下的内容:
- 工具 / 框架的应用办法
- 主动生成的模版以及如何进行简略的开发
- 综合比照
框架的应用和代码剖析
ETH hardhat
官网 doc
官网 Tutorial
环境配置:因为须要应用 Ethers.js 进行测试和交互,所以须要装置 node.js;
装置:npm install --save-dev hardhat
初始化我的项目:在我的项目中执行 npx hardhat
,通过提醒,抉择本人要应用的模版,而后会在根目录中创立必要的文件和目录;
.
├── README.md
├── contracts
│ └── Greeter.sol
├── hardhat.config.js
├── node_modules
├── package-lock.json
├── package.json
├── scripts
│ └── sample-script.js
└── test
└── sample-test.js
能够看到框架曾经帮咱们分好了目录,其中 contracts 目录下是具体的 solidity 合约代码;scripts 中是部署合约须要的代码;test 中是测试代码;
编译:npx hardhat complie
部署: npx hardhat run scripts/deploy.js --network <network-name>
- 不带 –network 参数,会应用 hardhat 自带的默认网络,
- 应用 remote 的网络(测试 / 正式网络)须要批改
hardhat,config.js
文件,具体的批改代码如下;
require("@nomiclabs/hardhat-waffle");
// Go to https://www.alchemyapi.io, sign up, create
// a new App in its dashboard, and replace "KEY" with its key
const ALCHEMY_API_KEY = "KEY";
// Replace this private key with your Ropsten account private key
// To export your private key from Metamask, open Metamask and
// go to Account Details > Export Private Key
// Be aware of NEVER putting real Ether into testing accounts
const ROPSTEN_PRIVATE_KEY = "YOUR ROPSTEN PRIVATE KEY";
module.exports = {
solidity: "0.8.4",
networks: {
ropsten: {url: `https://eth-ropsten.alchemyapi.io/v2/${ALCHEMY_API_KEY}`,
accounts: [`${ROPSTEN_PRIVATE_KEY}`]
}
}
};
测试:npx hardhat test
因为 hardhat 自带本地 ETH 网络,所以不须要启动节点能够通过 web3 接口进行测试;
Debug:
1. 在合约代码中增加 ```import "hardhat/console.sol";``` 导入日志工具;2. 在须要应用的代码中通过 ```console.log("info %s", to);``` 的形式打印一些须要的调试信息;3. 最初应用 ``` npx hardhat test``` 会输入调试信息;
Solana Anchor
官网文档
依赖装置配置:
- 装置 rust
- 装置 solana
- 装置 node.js 和 yarn 并且换源
- 装置 anchor
初始化我的项目:
运行 anchor init <new-project-name>
.
├── Anchor.toml // Anchor 配置文件
├── Cargo.toml // Rust 工作区配置文件。├── app // 应用程序前端的目录
├── migrations // 合约迁徙部署的代码
│ └── deploy.ts
├── node_modules
├── package.json
├── programs // 合约逻辑代码
│ └── test
│ ├── Cargo.toml
│ ├── Xargo.toml
│ └── src
└── lib.rs
├── tests // 合约测试
│ └── test.ts
├── tsconfig.json
└── yarn.lock
编译:anchor build
编译的命令是上面两条命令的组合;1. `cargo build-bpf`
2. `anchor idl parse -f program/src/lib.rs -o target/idl/basic_0.json`
测试:anchor test
部署:anchor deploy
Anchor.toml 文件中能够定义部署的网络环境和钱包信息等数据
[provider]
cluster = "localnet"
wallet = "~/.config/solana/id.json"
示例代码
use anchor_lang::prelude::*;
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
mod basic_1 {
use super::*;
pub fn initialize(ctx: Context<Initialize>, data: u64) -> ProgramResult {
let my_account = &mut ctx.accounts.my_account;
my_account.data = data;
Ok(())
}
pub fn update(ctx: Context<Update>, data: u64) -> ProgramResult {
let my_account = &mut ctx.accounts.my_account;
my_account.data = data;
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {#[account(init, payer = user, space = 8 + 8)]
pub my_account: Account<'info, MyAccount>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Update<'info> {#[account(mut)]
pub my_account: Account<'info, MyAccount>,
}
#[account]
pub struct MyAccount {pub data: u64,}
#[error]
pub enum ErrorCode {#[msg("This account cannot update")]
CannotUpdate,
}
示例代码解析
- #[program] 上面的 mod 中蕴含了 instructions,如果你不理解 Solana 中的 instruction,能够看一下官网文档;
- #[derive(Accounts)] 蕴含了账户信息,solana 中的账户有点绕,你能够了解为 Linux 中所有皆文件的概念,具体的也须要你看官网文档;
-
#[account] 账户属性,账户初始化的时候须要领取租金和指定 size;
#[account(init, payer = user, space = 8 + 8)] #[account(mut)]
-
上面两个派生宏的组合能够自定义错误信息
- #[error]
- #[msg(“detail error info”)]
ink!
官网 Doc
装置依赖:
- 装置 rust
- 装置 cargo 和 wasm 依赖
- 装置 contracts-node
初始化我的项目:
应用命令创立新的合约我的项目:cargo contract new <new project name>
flipper
└─ lib.rs <-- Contract Source Code
└─ Cargo.toml <-- Rust Dependencies and ink! Configuration
└─ .gitignore
编译:cargo +nightly contract build
测试:cargo +nightly test
部署:间接在节点的 UI 界面上传编译之后的文件(target 目录下的.contract 结尾的文件),substrate 的部署和运行时离开的,同一份代码只能部署一份,然而能够有很多的运行实例;
示例代码:
// We are importing the default ink! types
use ink_lang as ink;
#[ink::contract]
mod MyContract {
// Our struct will use those default ink! types
#[ink(storage)]
pub struct MyContract {number: u32,}
impl MyContract {
/// Constructor that initializes the `u32` value to the given `init_value`.
#[ink(constructor)]
pub fn new(init_value: u32) -> Self {
Self {number: init_value,}
}
#[ink(message)]
pub fn my_public_function(&self) {/* --snip-- */}
/// Private function
fn my_private_function(&self) {/* --snip-- */}
}
}
self.env().emit_event() // 用于收回 event,向外界提示信息,能够认为和 Solana Anchor 中的 console.log 相似
self.env().caller() // 示意合约的调用者,solana 中会应用 programID 来示意;
派生宏介绍
- #[ink::contract] 用于合约的 mod
- #[ink(storage)] 用户合约中的数据存储,具体反对的数据类型,会对 rust 原生类型做一些封装;
- #[ink(constructor) 用户初始化结构,不同与以太坊,这里能够有多个 constructor;
- #[ink(message)] 用于 constuctor 之外的 pub 函数;
总提比照
平台 / 框架 | 代码模版劣势 | 特点 | 难度 |
---|---|---|---|
ETH / Hardhat | 包含部署目录,合约目录,测试目录,拆分比拟具体,构造很清晰 | 次要是对代码模块逻辑进行拆分,没有对 solidity 进行过多的封装,原生迁徙比拟容易; | 须要理解 solidity 和 javascript |
Solana / Anchor | 包含部署目录,合约目录,测试目录,拆分比拟具体,构造很清晰 | 进行了很多的宏封装,将很多 instructions 和 account 进行包装,尽管简化了开发难度和代码量,然而对与刚理解 solana 的开发者不太容易了解细节; | 须要理解 solana 的交易和 account 含意,并且要会 rust |
Substrate / ink! | 单纯的蕴含了合约代码,比拟简洁 | 进行了一些宏封装,简化了开发难度,然而自由度比拟高,不必太局限于太多的链细节; | 须要会 rust,对小白用户比拟敌对 |