{"id":20858932,"url":"https://github.com/secretshardul/near-rust-tutorial","last_synced_at":"2025-10-10T09:10:49.896Z","repository":{"id":101006177,"uuid":"359855636","full_name":"secretshardul/near-rust-tutorial","owner":"secretshardul","description":"Near rust tutorial project (https://docs.near.org/docs/develop/contracts/rust/intro)","archived":false,"fork":false,"pushed_at":"2021-04-23T14:54:39.000Z","size":56,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-29T06:41:00.036Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/secretshardul.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-04-20T15:00:36.000Z","updated_at":"2021-04-23T14:54:41.000Z","dependencies_parsed_at":null,"dependency_job_id":"921e8c4d-7adb-4b64-a9e1-9029d4f51d87","html_url":"https://github.com/secretshardul/near-rust-tutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/secretshardul/near-rust-tutorial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/secretshardul%2Fnear-rust-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/secretshardul%2Fnear-rust-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/secretshardul%2Fnear-rust-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/secretshardul%2Fnear-rust-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/secretshardul","download_url":"https://codeload.github.com/secretshardul/near-rust-tutorial/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/secretshardul%2Fnear-rust-tutorial/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279003389,"owners_count":26083579,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-18T04:48:06.820Z","updated_at":"2025-10-10T09:10:49.879Z","avatar_url":"https://github.com/secretshardul.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Near Rust tutorial\n\n## Directory structure\n\nSetup rust environment using `cargo init --lib`\n\n```\n.\n├── Cargo.toml\n└── src\n   └── lib.rs\n```\n\n- **Cargo.toml**: Similar to `package.json` in Node. It contains dependency list, build settings and package metadata.\n- **lib.rs**: Contains contract.\n\n## Contract structure\n\nLook at [src/lib.rs](./src/lib.rs) for notes.\n\n## Commands\n```sh\n# Compile\nenv 'RUSTFLAGS=-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release\n\n# Create deployment account (optional)\nnear create-account counter.monkeyis.testnet --masterAccount=monkeyis.testnet  --initialBalance 10000000\n\n# Deploy contract to account\n# Unlike Ethereum, the contract gets deployed to the same account\nnear deploy --wasmFile target/wasm32-unknown-unknown/release/near_rust_tutorial.wasm --accountId counter.monkeyis.testnet\n\n# Calling contract functions\n\n# Use 'call' for functions that change state\nnear call monkeyis.testnet increment --accountId monkeyis.testnet\nnear call monkeyis.testnet decrement --accountId monkeyis.testnet\n\n# Use 'view' for read-only functions\nnear view monkeyis.testnet get_num --accountId monkeyis.testnet\n```\n\n## Testing\n\nLook at [src/lib.rs](./src/lib.rs) for boilerplate and test code structure. Run tests using `cargo test` command or by pressing run test buttons inside VS Code.\n\n## Cross contract functionality\n\n### 1. Deploying another contract\n\nInstances of contract B will be deployed by contract A.\n\n1. Write and compile contract B in a separate folder.\n2. Copy the compiled `.wasm` file to contract A's directory\n3. Source code to deploy contract from contract:\n\n   ```rs\n    pub fn deploy_contract(\u0026self, account_id: String, amount: U128) {\n        Promise::new(account_id)\n            .create_account()\n            .transfer(amount.0)\n            .add_full_access_key(env::signer_account_pk())\n            .deploy_contract(\n                /* Path to compiled .wasm file of contract  */\n                include_bytes!(\"./postbox_contract.wasm\").to_vec(),\n            );\n    }\n   ```\n\n**Note**: A newly created account must be under a namespace of the creator account. Suppose contract A is deployed to `A.testnet` then B must be deployed to `B.A.testnet`. Otherwise `CreateAccountNotAllowed` will be thrown.\n\n4. Call the `deploy_contract` function on deployed contract `counter.monkeyis.testnet`\n\n```sh\n# Deploy contract to address crossctr.counter.monkeyis.testnet with starting balance of 10 Near\nnear call counter.monkeyis.testnet deploy_contract '{ \"account_id\": \"gg.counter.monkeyis.testnet\", \"amount\": \"10000000000000000000000000\" }' --accountId counter.monkeyis.testnet\n```\n\n5. Querying the newly created contract\n\n```sh\nnear view gg.counter.monkeyis.testnet get_message --accountId monkeyis.testnet\n```\n\n### 2. Transfer money to and between contracts\n\n**Code**\n\n```rs\n#[payable] // Attribute is needed to accept payments. Methods are non-payable by default.\npub fn transfer_money(\u0026mut self, account_id: String) {\n   let deposit = env::attached_deposit(); // Read transferred amount\n\n   // Contract to contract transfer\n   Promise::new(account_id).transfer(\n      deposit\n   );\n}\n```\n\nWe are making two transfers here:\n- **Transfer from user's account to contract**\n\n   ```sh\n   near call counter.monkeyis.testnet transfer_money '{ \"account_id\": \"gg.counter.monkeyis.testnet\" }' --accountId counter.monkeyis.testnet --amount 2\n   ```\n\n- **Contract to contract transfer**\n\n### 3. Cross contract calls using high level API\n\nTwo types of calls are possible:\n- **Update state** of callee contract\n- **Read state** of callee contract: We can either\n   - Just return the obtained value\n   - Perform computation on this value and return the result.\n\n### Updating state of callee contract\n\n1. Create a trait with `#[ext_contract]` which contains method signatures of callee contract.\n\n   ```rs\n   #[ext_contract]\n   pub trait Postbox {\n      fn set_message(\u0026mut self, message: u8);\n      fn get_message(\u0026self) -\u003e u8;\n   }\n   ```\n\n2. This generates a snake case namespace which lets us call the other contract.\n\n   ```rs\n   postbox_contract::set_message(\n      message, // Parameters for set_message() function\n      \u0026account_id, // Postbox contract address\n      0, // Amount to send\n      env::prepaid_gas() / 2 // Gas\n   );\n   ```\n\n3. Trigger this call using Near CLI\n\n   ```sh\n   near call counter.monkeyis.testnet set_postbox_message '{ \"account_id\": \"gg.counter.monkeyis.testnet\", \"message\": 5 }' --accountId counter.monkeyis.testnet\n   ```\n\n### Reading value from callee contract\n\n1. To just read and return the value:\n\n   ```rs\n    pub fn get_postbox_message(\u0026self, account_id: String) -\u003e Promise {\n        // Read from postbox contract and return value as result\n        postbox::get_message(\n            \u0026account_id,\n            0,\n            env::prepaid_gas() / 2\n        )\n    }\n   ```\n\n   ```sh\n   near call counter.monkeyis.testnet get_postbox_message '{ \"account_id\": \"gg.counter.monkeyis.testnet\" }' --accountId monkeyis.testnet\n   ```\n\n   **Note**: Return type must be `Promise`\n\n2. To perform operations on the returned value, we need a callback to handle the promise.\n\n   - Create a trait for the current contract, containing signature of the callback function.\n\n   ```rs\n   #[ext_contract]\n   pub trait Counter {\n      #[result_serializer(borsh)]\n      fn postbox_callback(\n         \u0026self,\n         #[callback]\n         message: u8\n      ) -\u003e u8;\n   }\n   ```\n\n   - `PromiseOrValue`, `then()` and `into()` are used to pass the results into the callback function\n\n   ```rs\n   pub fn process_postbox_message(\u0026self, account_id: String) -\u003e PromiseOrValue\u003cu8\u003e {\n      let call_fee = env::prepaid_gas() / 4;\n\n      postbox::get_message(\n         \u0026account_id,\n         0,\n         call_fee\n      ).then(counter::postbox_callback(\n         // then() automatically passes the results into current_account_id()\n         // No need to explicitly specify the parameters\n         \u0026env::current_account_id(),\n         0,\n         call_fee\n      )).into() // convert Promise into PromiseOrValue\u003cu8\u003e\n   }\n   ```\n\n   - Create the callback function. Note use use of `#[callback]` attribute.\n\n   ```rs\n   #[private] // Should be callable from current contract\n   pub fn postbox_callback(\n      \u0026self,\n      #[callback] // Necessary for then() to pass results into this function\n      message: u8\n   ) -\u003e u8 {\n      log!(\"Got postbox message {:?}\", message);\n\n      message + 1\n   }\n   ```\n\n#### TODO\n\n- Is it possible to use the callback function without creating a trait for the contract? The contract implementation already contains the callback function signature.\n- The docs use `#[result_serializer(borsh)]` and `#[serializer(borsh)]` to optimize the callback function. This was skipped due to number conversion issue when logging (0 -\u003e 48), although correct value was returned. Near SDK falls back to JSON serializer by default. Find how to implement this. https://github.com/near/near-sdk-rs/blob/97fc632fcc58eb7ff7faad0be54ea8ec91dbf694/examples/cross-contract-high-level/src/lib.rs#L98\n\n## Rust notes\n\n### Rust binary vs library\n1. Binaries are standalone, created using `cargo init`. They have a **main.rs**.\n2. Libraries: Used by other programs. Create using `cargo init --lib`. They have a **lib.rs** file.\n\n### Installing dependencies\nUse either:\n1. `cargo install ABC`: For dependencies having binaries\n2. Add dependency name and version to `[dependencies]` section of **Cargo.toml**. For Near SDK add `near-sdk = \"3.1.0\"`\n\n\n### Rust attributes\n\nEg. `#[near_bindgen]`\n\nAn attribute is metadata applied to an item. It can be used for:\n- Conditional compilation\n- Set crate name, version etc\n- Disabling lints\n- Linking to a foreign library\n- Marking functions as unit tests\n\n### Syntax\n```rs\n#[attribute(value)]\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecretshardul%2Fnear-rust-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsecretshardul%2Fnear-rust-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsecretshardul%2Fnear-rust-tutorial/lists"}