Skip to content

Commit

Permalink
feat: Introduce #[near] macro to further streamline contracts devel…
Browse files Browse the repository at this point in the history
…opment reducing the boilerplate! (#1142)
  • Loading branch information
PolyProgrammist committed Mar 15, 2024
1 parent 9bb16c2 commit 1408127
Show file tree
Hide file tree
Showing 80 changed files with 678 additions and 542 deletions.
18 changes: 16 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ jobs:
- uses: Swatinem/rust-cache@v1
- name: Downgrade dependencies
run: |
cargo update -p clap@4.5.1 --precise 4.4.18
cd examples/adder && cargo update -p clap@4.5.1 --precise 4.4.18
cargo update -p clap@4.5.3 --precise 4.4.18
cd examples/adder && cargo update -p clap@4.5.3 --precise 4.4.18
- name: test
run: cargo test --all --features unstable,legacy
lint:
Expand All @@ -46,6 +46,20 @@ jobs:
- name: Test Format
run: cargo fmt -- --check
- run: cargo clippy --tests --all-features -- -Dclippy::all
compilation:
name: Compilation tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
default: true
- uses: Swatinem/rust-cache@v1
- name: Compilation tests
run: cargo test --package near-sdk --test compilation_tests --features abi --features unstable -- compilation_tests --exact --nocapture
windows:
name: Windows
runs-on: windows-latest
Expand Down
30 changes: 13 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,17 @@

## Example

Wrap a struct in `#[near_bindgen]` and it generates a smart contract compatible with the NEAR blockchain:
Wrap a struct in `#[near]` and it generates a smart contract compatible with the NEAR blockchain:
```rust
use near_sdk::{near_bindgen, env};
use near_sdk::{near, env};

#[near_bindgen]
#[derive(Default, BorshDeserialize, BorshSerialize)]
#[borsh(crate = "near_sdk::borsh")]
#[near(contract_state)]
#[derive(Default)]
pub struct StatusMessage {
records: HashMap<AccountId, String>,
}

#[near_bindgen]
#[near]
impl StatusMessage {
pub fn set_status(&mut self, message: String) {
let account_id = env::signer_account_id();
Expand Down Expand Up @@ -97,7 +96,7 @@ to see various usages of cross contract calls, including **system-level actions*
We can define an initialization method that can be used to initialize the state of the contract. `#[init]` verifies that the contract has not been initialized yet (the contract state doesn't exist) and will panic otherwise.

```rust
#[near_bindgen]
#[near]
impl StatusMessage {
#[init]
pub fn new(user: String, status: String) -> Self {
Expand All @@ -119,9 +118,8 @@ impl Default for StatusMessage {
```
You can also prohibit `Default` trait initialization by using `near_sdk::PanicOnDefault` helper macro. E.g.:
```rust
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
#[borsh(crate = "near_sdk::borsh")]
#[near(contract_state)]
#[derive(PanicOnDefault)]
pub struct StatusMessage {
records: HashMap<String, String>,
}
Expand Down Expand Up @@ -183,15 +181,13 @@ The general workflow is the following:
2. Crate needs to have one `pub` struct that will represent the smart contract itself:
* The struct needs to implement `Default` trait which
NEAR will use to create the initial state of the contract upon its first usage;
* The struct also needs to implement `BorshSerialize` and `BorshDeserialize` traits which NEAR will use to save/load contract's internal state;

Here is an example of a smart contract struct:
```rust
use near_sdk::{near_bindgen, env};
use near_sdk::{near, env};

#[near_bindgen]
#[derive(Default, BorshSerialize, BorshDeserialize)]
#[borsh(crate = "near_sdk::borsh")]
#[near(contract_state)]
#[derive(Default)]
pub struct MyContract {
data: HashMap<u64, u64>
}
Expand All @@ -200,12 +196,12 @@ The general workflow is the following:
3. Define methods that NEAR will expose as smart contract methods:
* You are free to define any methods for the struct but only public methods will be exposed as smart contract methods;
* Methods need to use either `&self`, `&mut self`, or `self`;
* Decorate the `impl` section with `#[near_bindgen]` macro. That is where all the M.A.G.I.C. (Macros-Auto-Generated Injected Code) happens;
* Decorate the `impl` section with `#[near]` macro. That is where all the M.A.G.I.C. (Macros-Auto-Generated Injected Code) happens;
* If you need to use blockchain interface, e.g. to get the current account id then you can access it with `env::*`;

Here is an example of smart contract methods:
```rust
#[near_bindgen]
#[near]
impl MyContract {
pub fn insert_data(&mut self, key: u64, value: u64) -> Option<u64> {
self.data.insert(key)
Expand Down
20 changes: 6 additions & 14 deletions examples/adder/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
use near_sdk::borsh::{BorshDeserialize, BorshSerialize};
use near_sdk::serde::{Deserialize, Serialize};
use near_sdk::{near_bindgen, NearSchema};
use near_sdk::near;

#[derive(NearSchema, Serialize, Deserialize, BorshDeserialize, BorshSerialize)]
#[borsh(crate = "near_sdk::borsh")]
#[serde(crate = "near_sdk::serde")]
#[abi(json, borsh)]
#[near(serializers=[borsh, json])]
pub struct Pair(u32, u32);

#[derive(NearSchema, Serialize, Deserialize)]
#[serde(crate = "near_sdk::serde")]
#[abi(json, borsh)]
#[near(serializers=[borsh, json])]
pub struct DoublePair {
first: Pair,
second: Pair,
}

#[near_bindgen]
#[derive(Default, BorshDeserialize, BorshSerialize)]
#[borsh(crate = "near_sdk::borsh")]
#[derive(Default)]
#[near(serializers=[borsh, json], contract_state)]
pub struct Adder {}

#[near_bindgen]
#[near]
impl Adder {
/// Adds two pairs point-wise.
pub fn add(&self, a: Pair, b: Pair) -> Pair {
Expand Down
6 changes: 3 additions & 3 deletions examples/callback-results/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use near_sdk::require;
use near_sdk::{env, near_bindgen, Promise, PromiseError};
use near_sdk::{env, near, Promise, PromiseError};

const A_VALUE: u8 = 8;

#[near_bindgen]
#[near(contract_state)]
pub struct Callback;

#[near_bindgen]
#[near]
impl Callback {
/// Call functions a, b, and c asynchronously and handle results with `handle_callbacks`.
pub fn call_all(fail_b: bool, c_value: u8, d_value: u8) -> Promise {
Expand Down
10 changes: 4 additions & 6 deletions examples/cross-contract-calls/high-level/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use near_sdk::borsh::{BorshDeserialize, BorshSerialize};
use near_sdk::env;
use near_sdk::{log, near_bindgen, PromiseOrValue};
use near_sdk::{log, near, PromiseOrValue};

#[near_bindgen]
#[derive(Default, BorshDeserialize, BorshSerialize)]
#[borsh(crate = "near_sdk::borsh")]
#[derive(Default)]
#[near(contract_state)]
pub struct CrossContract {}

#[near_bindgen]
#[near]
impl CrossContract {
pub fn factorial(&self, n: u32) -> PromiseOrValue<u32> {
if n <= 1 {
Expand Down
10 changes: 4 additions & 6 deletions examples/cross-contract-calls/low-level/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
use near_sdk::borsh::{BorshDeserialize, BorshSerialize};
use near_sdk::serde_json;
use near_sdk::{env, near_bindgen, require, Gas, NearToken, PromiseResult};
use near_sdk::{env, near, require, Gas, NearToken, PromiseResult};

// Prepaid gas for a single (not inclusive of recursion) `factorial` call.
const FACTORIAL_CALL_GAS: Gas = Gas::from_tgas(20);

// Prepaid gas for a single `factorial_mult` call.
const FACTORIAL_MULT_CALL_GAS: Gas = Gas::from_tgas(10);

#[near_bindgen]
#[derive(Default, BorshDeserialize, BorshSerialize)]
#[borsh(crate = "near_sdk::borsh")]
#[derive(Default)]
#[near(contract_state)]
pub struct CrossContract {}

#[near_bindgen]
#[near]
impl CrossContract {
pub fn factorial(&self, n: u32) {
if n <= 1 {
Expand Down
10 changes: 4 additions & 6 deletions examples/factory-contract/high-level/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use near_sdk::borsh::{BorshDeserialize, BorshSerialize};
use near_sdk::PromiseError;
use near_sdk::{env, ext_contract, near_bindgen, AccountId, NearToken, Promise};
use near_sdk::{env, ext_contract, near, AccountId, NearToken, Promise};

#[near_bindgen]
#[derive(Default, BorshDeserialize, BorshSerialize)]
#[borsh(crate = "near_sdk::borsh")]
#[derive(Default)]
#[near(contract_state)]
pub struct FactoryContract {}

// If the `ext_contract` name is not provided explicitly, the namespace for generated methods is
Expand All @@ -15,7 +13,7 @@ pub trait ExtStatusMessage {
fn get_status(&self, account_id: AccountId) -> Option<String>;
}

#[near_bindgen]
#[near]
impl FactoryContract {
pub fn deploy_status_message(&self, account_id: AccountId, amount: NearToken) {
Promise::new(account_id)
Expand Down
10 changes: 4 additions & 6 deletions examples/factory-contract/low-level/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
use near_sdk::borsh::{BorshDeserialize, BorshSerialize};
use near_sdk::json_types::U128;
use near_sdk::serde_json;
use near_sdk::{env, near_bindgen, AccountId, Gas, NearToken, PromiseResult};
use near_sdk::{env, near, AccountId, Gas, NearToken, PromiseResult};

// Prepaid gas for making a single simple call.
const SINGLE_CALL_GAS: Gas = Gas::from_tgas(20);

#[near_bindgen]
#[derive(Default, BorshDeserialize, BorshSerialize)]
#[borsh(crate = "near_sdk::borsh")]
#[derive(Default)]
#[near(contract_state)]
pub struct FactoryContract {}

#[near_bindgen]
#[near]
impl FactoryContract {
pub fn deploy_status_message(&self, account_id: AccountId, amount: U128) {
let promise_idx = env::promise_batch_create(&account_id);
Expand Down
20 changes: 9 additions & 11 deletions examples/fungible-token/ft/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,15 @@ use near_contract_standards::fungible_token::{
use near_contract_standards::storage_management::{
StorageBalance, StorageBalanceBounds, StorageManagement,
};
use near_sdk::borsh::{BorshDeserialize, BorshSerialize};
use near_sdk::borsh::BorshSerialize;
use near_sdk::collections::LazyOption;
use near_sdk::json_types::U128;
use near_sdk::{
env, log, near_bindgen, require, AccountId, BorshStorageKey, NearToken, PanicOnDefault,
PromiseOrValue,
env, log, near, require, AccountId, BorshStorageKey, NearToken, PanicOnDefault, PromiseOrValue,
};

#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
#[borsh(crate = "near_sdk::borsh")]
#[derive(PanicOnDefault)]
#[near(contract_state)]
pub struct Contract {
token: FungibleToken,
metadata: LazyOption<FungibleTokenMetadata>,
Expand All @@ -49,7 +47,7 @@ enum StorageKey {
Metadata,
}

#[near_bindgen]
#[near]
impl Contract {
/// Initializes the contract with the given total supply owned by the given `owner_id` with
/// default metadata (for example purposes only).
Expand Down Expand Up @@ -94,7 +92,7 @@ impl Contract {
}
}

#[near_bindgen]
#[near]
impl FungibleTokenCore for Contract {
#[payable]
fn ft_transfer(&mut self, receiver_id: AccountId, amount: U128, memo: Option<String>) {
Expand All @@ -121,7 +119,7 @@ impl FungibleTokenCore for Contract {
}
}

#[near_bindgen]
#[near]
impl FungibleTokenResolver for Contract {
#[private]
fn ft_resolve_transfer(
Expand All @@ -139,7 +137,7 @@ impl FungibleTokenResolver for Contract {
}
}

#[near_bindgen]
#[near]
impl StorageManagement for Contract {
#[payable]
fn storage_deposit(
Expand Down Expand Up @@ -175,7 +173,7 @@ impl StorageManagement for Contract {
}
}

#[near_bindgen]
#[near]
impl FungibleTokenMetadataProvider for Contract {
fn ft_metadata(&self) -> FungibleTokenMetadata {
self.metadata.get().unwrap()
Expand Down
14 changes: 6 additions & 8 deletions examples/fungible-token/test-contract-defi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@
Some hypothetical DeFi contract that will do smart things with the transferred tokens
*/
use near_contract_standards::fungible_token::{receiver::FungibleTokenReceiver, Balance};
use near_sdk::borsh::{BorshDeserialize, BorshSerialize};
use near_sdk::json_types::U128;
use near_sdk::{env, log, near_bindgen, require, AccountId, Gas, PanicOnDefault, PromiseOrValue};
use near_sdk::{env, log, near, require, AccountId, Gas, PanicOnDefault, PromiseOrValue};

const BASE_GAS: u64 = 5_000_000_000_000;
const PROMISE_CALL: u64 = 5_000_000_000_000;
const GAS_FOR_FT_ON_TRANSFER: Gas = Gas::from_gas(BASE_GAS + PROMISE_CALL);

#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
#[borsh(crate = "near_sdk::borsh")]
#[derive(PanicOnDefault)]
#[near(contract_state)]
pub struct DeFi {
fungible_token_account_id: AccountId,
}
Expand All @@ -22,7 +20,7 @@ trait ValueReturnTrait {
fn value_please(&self, amount_to_return: String) -> PromiseOrValue<U128>;
}

#[near_bindgen]
#[near]
impl DeFi {
#[init]
pub fn new(fungible_token_account_id: AccountId) -> Self {
Expand All @@ -31,7 +29,7 @@ impl DeFi {
}
}

#[near_bindgen]
#[near]
impl FungibleTokenReceiver for DeFi {
/// If given `msg: "take-my-money", immediately returns U128::From(0)
/// Otherwise, makes a cross-contract call to own `value_please` function, passing `msg`
Expand Down Expand Up @@ -62,7 +60,7 @@ impl FungibleTokenReceiver for DeFi {
}
}

#[near_bindgen]
#[near]
impl ValueReturnTrait for DeFi {
fn value_please(&self, amount_to_return: String) -> PromiseOrValue<U128> {
log!("in value_please, amount_to_return = {}", amount_to_return);
Expand Down
14 changes: 6 additions & 8 deletions examples/lockable-fungible-token/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use near_sdk::borsh::{BorshDeserialize, BorshSerialize};
use near_sdk::collections::UnorderedMap;
use near_sdk::{env, json_types::U128, near_bindgen, AccountId, PanicOnDefault};
use near_sdk::{env, json_types::U128, near, AccountId, PanicOnDefault};
use std::collections::HashMap;

type Balance = u128;

#[derive(Default, BorshDeserialize, BorshSerialize)]
#[borsh(crate = "near_sdk::borsh")]
#[derive(Default)]
#[near]
pub struct Account {
/// Current unlocked balance
pub balance: Balance,
Expand Down Expand Up @@ -46,9 +45,8 @@ impl Account {
}
}

#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)]
#[borsh(crate = "near_sdk::borsh")]
#[derive(PanicOnDefault)]
#[near(contract_state)]
pub struct FunToken {
/// AccountID -> Account details.
pub accounts: UnorderedMap<AccountId, Account>,
Expand All @@ -57,7 +55,7 @@ pub struct FunToken {
pub total_supply: Balance,
}

#[near_bindgen]
#[near]
impl FunToken {
#[init]
#[handle_result]
Expand Down
Loading

0 comments on commit 1408127

Please sign in to comment.