45个版本 (14个稳定版)

5.3.0 2024年8月14日
5.2.1 2024年7月5日
5.1.0 2024年3月28日
5.0.0-alpha.12023年11月19日
0.7.0 2020年3月27日

#180 in 魔法豆

Download history 1843/week @ 2024-05-01 2115/week @ 2024-05-08 2255/week @ 2024-05-15 2360/week @ 2024-05-22 2774/week @ 2024-05-29 2069/week @ 2024-06-05 1697/week @ 2024-06-12 1977/week @ 2024-06-19 1953/week @ 2024-06-26 1100/week @ 2024-07-03 1834/week @ 2024-07-10 2235/week @ 2024-07-17 2137/week @ 2024-07-24 2356/week @ 2024-07-31 2576/week @ 2024-08-07 2268/week @ 2024-08-14

9,615 每月下载量
用于 45 个crate(44个直接使用)

MIT/Apache

1MB
20K SLoC

near-sdk

Rust库,用于编写NEAR智能合约。

之前被称为 near-bindgen

Reference Documentation MSRV Crates.io version Download Join the community on Discord Join the community on Telegram

特性 | 先决条件 | 编写Rust合约 | 构建Rust合约 | 参考文档 | 贡献

发行说明

发行说明和未发布的变更可以在 CHANGELOG 中找到

示例

使用 #[near] 包装一个结构体,并生成与NEAR区块链兼容的智能合约

use near_sdk::{near, env};

#[near(contract_state)]
#[derive(Default)]
pub struct StatusMessage {
    records: HashMap<AccountId, String>,
}

#[near]
impl StatusMessage {
    pub fn set_status(&mut self, message: String) {
        let account_id = env::signer_account_id();
        self.records.insert(account_id, message);
    }

    pub fn get_status(&self, account_id: AccountId) -> Option<String> {
        self.records.get(&account_id).cloned()
    }
}

特性

可编写单元测试

使用 near-sdk 编写单元测试很简单

#[test]
fn set_get_message() {
    let mut contract = StatusMessage::default();
    contract.set_status("hello".to_string());
    assert_eq!("hello".to_string(), contract.get_status("bob_near".to_string()).unwrap());
}

按常规方式运行单元测试

cargo test --package status-message

异步跨合约调用

异步跨合约调用允许并行执行多个合约,随后在另一个合约上进行聚合。env公开了以下方法

  • promise_create -- 在某个合约上安排函数执行;
  • promise_then -- 函数执行后,将回调附加到当前合约;
  • promise_and -- 组合器,允许在执行回调之前同时等待多个承诺;
  • promise_return -- 将承诺的执行结果视为当前函数的结果。

参考examples/cross-contract-high-level,了解跨合约调用的各种用法,包括在合约内部完成的系统级操作(如余额转移等系统级操作的示例包括:账户创建、访问密钥创建/删除、合约部署等)。

初始化方法

我们可以定义一个初始化方法,用于初始化合约的状态。当合约尚未初始化(合约状态不存在)时,#[init]会验证合约的状态,否则将引发恐慌。

#[near]
impl StatusMessage {
    #[init]
    pub fn new(user: String, status: String) -> Self {
        let mut res = Self::default();
        res.records.insert(user, status);
        res
    }
}

即使你有初始化方法,你的智能合约仍然需要实现Default特质。如果你不希望禁用默认初始化,那么你可以禁止它,如下所示

impl Default for StatusMessage {
    fn default() -> Self {
        near_sdk::env::panic_str("Contract should be initialized before the usage.")
    }
}

你也可以使用near_sdk::PanicOnDefault辅助宏来禁止Default特质的初始化。例如。

#[near(contract_state)]
#[derive(PanicOnDefault)]
pub struct StatusMessage {
    records: HashMap<String, String>,
}

可支付方法

我们可以允许方法在函数调用时接受代币转移。这样,合约可以定义在使用时需要支付代币的费用。默认情况下,方法不可支付,如果在调用过程中有人尝试向它们转移代币,则将引发恐慌。这是出于安全考虑,以防有人意外地在函数调用期间转移代币。

要声明一个可支付方法,请使用#[payable]装饰器


#[payable]
pub fn my_method(&mut self) {
...
}

私有方法

通常,当一个合约需要远程跨合约调用的回调时,这个回调方法应该只由合约本身调用。这是为了避免其他人调用它并破坏状态。常见的模式是使用断言来验证直接调用者(前驱账户ID)是否与合约的账户(当前账户ID)匹配。宏#[private]通过将其简化为一个单行宏来提高可读性。

要声明一个私有方法,请使用#[private]装饰器


#[private]
pub fn my_method(&mut self) {
...
}
/// Which is equivalent to

pub fn my_method(&mut self ) {
    if near_sdk::env::current_account_id() != near_sdk::env::predecessor_account_id() {
        near_sdk::env::panic_str("Method my_method is private");
    }
...
}

现在,只有合约的账户本身可以调用此方法,无论是直接调用还是通过承诺。

先决条件

要开发Rust合约,您需要

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  • 将wasm目标添加到您的工具链中
rustup target add wasm32-unknown-unknown

编写Rust合约

您可以参考examples/status-message存储库,该存储库展示了简单的Rust合约。

一般工作流程如下

  1. 创建一个存储库,并像在examples/status-message/Cargo.toml中配置的那样配置Cargo.toml

  2. 箱子需要有一个代表智能合约本身的 pub 结构体

    • 该结构体需要实现 Default 特性,NEAR 将使用它来创建合约的初始状态

    以下是一个智能合约结构体的示例

    use near_sdk::{near, env};
    
    #[near(contract_state)]
    #[derive(Default)]
    pub struct MyContract {
        data: HashMap<u64, u64>
    }
    
  3. 定义 NEAR 将公开为智能合约方法的方法

    • 您可以为结构体定义任何方法,但只有公开方法才会公开为智能合约方法
    • 方法需要使用 &self&mut selfself
    • 使用 #[near] 宏装饰 impl 部分。所有 M.A.G.I.C.(Macros-Auto-Generated Injected Code)都在这里发生
    • 如果您需要使用区块链接口,例如获取当前账户 ID,则可以使用 env::* 访问

    以下是一个智能合约方法的示例

    #[near]
    impl MyContract {
        pub fn insert_data(&mut self, key: u64, value: u64) -> Option<u64> {
            self.data.insert(key)
        }
        pub fn get_data(&self, key: u64) -> Option<u64> {
            self.data.get(&key).cloned()
        }
    }
    

构建 Rust 合约

cargo-near

cargo-near 是构建和部署 Rust 合约的一种简单且推荐的方法

安装

通过 shell 脚本安装预构建的二进制文件(Linux、macOS)
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/near/cargo-near/releases/latest/download/cargo-near-installer.sh | sh
通过 powershell 脚本安装预构建的二进制文件(Windows)
irm https://github.com/near/cargo-near/releases/latest/download/cargo-near-installer.ps1 | iex
将预构建的二进制文件安装到您的 Node.js 应用程序中
npm install cargo-near
从源代码编译和安装(Cargo)
cargo install cargo-near

或,从 git 仓库安装最新版本

$ git clone https://github.com/near/cargo-near
$ cargo install --path cargo-near

用法

请参阅 cargo near --help 以获取可用命令的完整列表,或运行 cargo near 以进入交互模式。每个单独的命令都提供帮助,例如使用 --help 标志,例如 cargo near build --help

cargo near

启动交互模式,允许探索所有可用命令

cargo near build

构建 NEAR 智能合约及其 ABI(在包含合约 Cargo.toml 的目录中)

cargo near create-dev-account

指导您在 testnet 上创建新的 NEAR 账户

cargo near deploy

构建智能合约(相当于 cargo near build)并指导您将其部署到区块链

使用 cargo build

RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release

使用可重现的构建进行构建

由于 WebAssembly 编译器会将大量调试信息包含到二进制文件中,因此在不同机器上生成的二进制文件可能不同。为了能够以可重现的方式编译二进制文件,我们添加了一个 Dockerfile,允许编译二进制文件。

使用 contract-builder

NEAR 合约标准

near-contract-standards crate 提供了 NEAR 合约标准的一组接口和实现

  • 可升级性
  • 可替换代币(NEP-141)。请参阅 示例用法
  • 非可替换代币(NEP-171)。请参阅 示例用法

版本控制

语义版本控制

此 crate 遵循 Cargo 的 semver 指南

应尽量避免状态破坏性变更(任何数据类型的低级序列化格式)。如果发生此类变更,它将以主版本号的形式出现,并伴随着编译器错误。如果您遇到不显示错误的情况,请提交一个issue

MSRV

当前最低支持的Rust版本为 1.76。如果需要发布一个需要增加Rust工具链的安全补丁版本,则不能保证这一版本的兼容性。

贡献

如果您有兴趣贡献,请查阅贡献指南

依赖

~3–18MB
~243K SLoC