diff --git a/index.mdx b/index.mdx
index dcf9c8f82d4..61f5edd0bad 100644
--- a/index.mdx
+++ b/index.mdx
@@ -27,6 +27,7 @@ mode: wide
Rust
JavaScript
Python
+ Go
Deploy your first contract in minutes
diff --git a/smart-contracts/anatomy/actions.mdx b/smart-contracts/anatomy/actions.mdx
index 11a2d9571a2..6f6d4deed9e 100644
--- a/smart-contracts/anatomy/actions.mdx
+++ b/smart-contracts/anatomy/actions.mdx
@@ -2,6 +2,9 @@
title: Transfers & Actions
description: "Learn how contracts can make transfers, call other contracts, and more"
---
+
+import { Github } from '/snippets/github.jsx'
+
This page describes the different types of actions that a smart contract can perform on NEAR like transferring NEAR, calling other contracts, creating sub-accounts, and deploying contracts. It also explains how to add access keys to accounts.
Smart contracts can perform specific `Actions` such as transferring NEAR, or calling other contracts.
@@ -20,7 +23,7 @@ but **cannot** call two methods on different contracts.
You can send `$NEAR` from your contract to any other account on the network. The Gas cost for transferring `$NEAR` is fixed and is based on the protocol's genesis config. Currently, it costs `~0.45 TGas`.
-
+
```rust
use near_sdk::{near, AccountId, Promise, NearToken};
@@ -37,9 +40,8 @@ You can send `$NEAR` from your contract to any other account on the network. The
}
```
-
-
-
+
+
```js
import { NearBindgen, NearPromise, call } from 'near-sdk-js'
@@ -54,9 +56,8 @@ You can send `$NEAR` from your contract to any other account on the network. The
}
```
-
-
-
+
+
```python
from near_sdk_py import call, Contract
@@ -70,42 +71,12 @@ class ExampleContract(Contract):
return Promise.create_batch(to).transfer(amount)
```
-
-
-
-
-```go
-package main
-
-import (
- "github.com/vlmoon99/near-sdk-go/promise"
- "github.com/vlmoon99/near-sdk-go/types"
-)
-
-// @contract:state
-type Contract struct{}
-
-type TransferTokenInput struct {
- To string `json:"to"`
- Amount string `json:"amount"`
-}
-
-// @contract:payable min_deposit=1NEAR
-func (c *Contract) ExampleTransferToken(input TransferTokenInput) error {
- amount, err := types.U128FromString(input.Amount)
- if err != nil {
- return err
- }
-
- promise.CreateBatch(input.To).
- Transfer(amount)
-
- return nil
-}
-```
-
-
-
+
+
+
+
@@ -126,7 +97,7 @@ in a deployed [Hello NEAR](../quickstart) contract, and check if everything went
right in the callback.
-
+
```rust
use near_sdk::{near, env, log, Promise, Gas, PromiseError};
@@ -164,9 +135,8 @@ right in the callback.
}
```
-
-
-
+
+
```js
import { NearBindgen, near, call, bytes, NearPromise } from 'near-sdk-js'
@@ -209,9 +179,8 @@ right in the callback.
}
```
-
-
-
+
+
```python
from near_sdk_py import call, callback, Contract
@@ -247,62 +216,12 @@ class ExampleContract(Contract):
return True
```
-
-
-
-
-```go
-package main
-
-import (
- "strconv"
-
- "github.com/vlmoon99/near-sdk-go/env"
- "github.com/vlmoon99/near-sdk-go/promise"
- "github.com/vlmoon99/near-sdk-go/types"
-)
-
-// @contract:state
-type Contract struct{}
-
-type MessageInput struct {
- Message string `json:"message"`
-}
-
-// @contract:payable min_deposit=0.00001NEAR
-func (c *Contract) ExampleFunctionCall() {
- gas := uint64(types.ONE_TERA_GAS * 10)
- accountId := "hello-nearverse.testnet"
- args := map[string]string{
- "message": "howdy",
- }
- promise.NewCrossContract(accountId).
- Gas(gas).
- Call("set_greeting", args).
- Then("example_function_call_callback", args)
-}
-
-// @contract:view
-// @contract:promise_callback
-func (c *Contract) ExampleFunctionCallCallback(input MessageInput, result promise.PromiseResult) MessageInput {
- env.LogString("Executing callback")
- env.LogString("Input Message : " + input.Message)
-
- if result.Success {
- env.LogString("Cross-contract call executed successfully")
- env.LogString("Promise Result Status --> " + strconv.FormatInt(int64(result.StatusCode), 10))
- if len(result.Data) > 0 {
- env.LogString("Batch call data: " + string(result.Data))
- }
- } else {
- env.LogString("Cross-contract call failed")
- }
- return input
-}
-
-```
-
-
+
+
+
+
@@ -319,7 +238,7 @@ Accounts do **NOT** have control over their sub-accounts, since they have their
Sub-accounts are simply useful for organizing your accounts (e.g. `dao.project.near`, `token.project.near`).
-
+
```rust
use near_sdk::{near, env, Promise, NearToken};
@@ -341,9 +260,8 @@ Sub-accounts are simply useful for organizing your accounts (e.g. `dao.project.n
}
```
-
-
-
+
+
```js
import { NearBindgen, near, call, NearPromise } from 'near-sdk-js'
@@ -363,9 +281,8 @@ Sub-accounts are simply useful for organizing your accounts (e.g. `dao.project.n
}
```
-
-
-
+
+
```python
from near_sdk_py import call, Contract
@@ -388,42 +305,12 @@ class ExampleContract(Contract):
.transfer(MIN_STORAGE)
```
-
-
-
-```go
-package main
-
-import (
- "github.com/vlmoon99/near-sdk-go/env"
- "github.com/vlmoon99/near-sdk-go/promise"
- "github.com/vlmoon99/near-sdk-go/types"
-)
-
-// @contract:state
-type Contract struct{}
-
-// @contract:payable min_deposit=0.001NEAR
-func (c *Contract) ExampleCreateSubaccount(prefix string) {
- currentAccountId, err := env.GetCurrentAccountId()
- if err != nil {
- env.PanicStr("Failed to get current account")
- }
-
- subaccountId := prefix + "." + currentAccountId
-
- amount, err := types.U128FromString("1000000000000000000000") //0.001Ⓝ
- if err != nil {
- env.PanicStr("Bad amount format")
- }
-
- promise.CreateBatch(subaccountId).
- CreateAccount().
- Transfer(amount)
-}
-```
-
-
+
+
+
+
@@ -446,7 +333,7 @@ If your contract wants to create a `.mainnet` or `.testnet` account, then it nee
the `create_account` method of `near` or `testnet` root contracts.
-
+
```rust
use near_sdk::{near, Promise, Gas, NearToken };
@@ -474,9 +361,8 @@ the `create_account` method of `near` or `testnet` root contracts.
}
```
-
-
-
+
+
```js
import { NearBindgen, near, call, bytes, NearPromise } from 'near-sdk-js'
@@ -499,9 +385,8 @@ the `create_account` method of `near` or `testnet` root contracts.
}
```
-
-
-
+
+
```python
from near_sdk_py import call, Contract
@@ -532,43 +417,12 @@ class ExampleContract(Contract):
)
```
-
-
-
-```go
-package main
-
-import (
- "github.com/vlmoon99/near-sdk-go/promise"
- "github.com/vlmoon99/near-sdk-go/types"
-)
-
-// @contract:state
-type Contract struct{}
-
-type CreateAccountInput struct {
- AccountId string `json:"account_id"`
- PublicKey string `json:"public_key"`
-}
-
-// @contract:payable min_deposit=0.002NEAR
-func (c *Contract) ExampleCreateAccount(args CreateAccountInput) {
- amount, _ := types.U128FromString("2000000000000000000000") // 0.002 NEAR
- gas := uint64(200 * types.ONE_TERA_GAS)
-
- //publicKey (base58) - 4omJwNS1WbniWtbPkLYBrFwN3YLeffXCkpvriYgeLhst (generate your own for testing)
- //accountId - nearsdkdocs1.testnet (write your own for testing)
- createArgs := map[string]string{
- "new_account_id": args.AccountId,
- "new_public_key": args.PublicKey,
- }
-
- promise.CreateBatch("testnet").
- FunctionCall("create_account", createArgs, amount, gas)
-}
-```
-
-
+
+
+
+
---
@@ -602,9 +456,8 @@ When creating an account you can also batch the action of deploying a contract t
}
```
-
-
-
+
+
```python
from near_sdk_py import call, Context, Contract
@@ -632,40 +485,12 @@ class ExampleContract(Contract):
.deploy_contract(contract_bytes)
```
-
-
-
-```go
-package main
-
-import (
- _ "embed"
-
- "github.com/vlmoon99/near-sdk-go/env"
- "github.com/vlmoon99/near-sdk-go/promise"
- "github.com/vlmoon99/near-sdk-go/types"
-)
-
-//go:embed status_message_go.wasm
-var contractWasm []byte
-
-// @contract:state
-type Contract struct{}
-
-// @contract:payable min_deposit=1.1NEAR
-func (c *Contract) ExampleDeployContract(prefix string) {
- currentAccountId, _ := env.GetCurrentAccountId()
- subaccountId := prefix + "." + currentAccountId
- amount, _ := types.U128FromString("1100000000000000000000000") // 1.1Ⓝ
-
- promise.CreateBatch(subaccountId).
- CreateAccount().
- Transfer(amount).
- DeployContract(contractWasm)
-}
-```
-
-
+
+
+
+
@@ -685,7 +510,7 @@ There are two options for adding keys to the account:
-
+
```rust
use near_sdk::{near, env, Promise, NearToken, PublicKey};
@@ -710,9 +535,8 @@ There are two options for adding keys to the account:
}
```
-
-
-
+
+
```js
import { NearBindgen, near, call, NearPromise } from 'near-sdk-js'
@@ -734,9 +558,8 @@ There are two options for adding keys to the account:
}
```
-
-
-
+
+
```python
from near_sdk_py import call, Contract
@@ -760,39 +583,12 @@ class ExampleContract(Contract):
.add_full_access_key(public_key)
```
-
-
-
-```go
-package main
-
-import (
- "github.com/vlmoon99/near-sdk-go/env"
- "github.com/vlmoon99/near-sdk-go/promise"
- "github.com/vlmoon99/near-sdk-go/types"
-)
-
-// @contract:state
-type Contract struct{}
-
-type AddKeysInput struct {
- Prefix string `json:"prefix"`
- PublicKey string `json:"public_key"`
-}
-
-// @contract:payable min_deposit=0.001NEAR
-func (c *Contract) ExampleAddKeys(input AddKeysInput) {
- currentAccountId, _ := env.GetCurrentAccountId()
- subaccountId := input.Prefix + "." + currentAccountId
- amount, _ := types.U128FromString("1000000000000000000000") // 0.001Ⓝ
-
- promise.CreateBatch(subaccountId).
- CreateAccount().
- Transfer(amount).
- AddFullAccessKey([]byte(input.PublicKey), 0)
-}
-```
-
+
+
+
+
Notice that what you actually add is a "public key". Whoever holds its private counterpart, i.e. the private-key, will be able to use the newly access key.
@@ -810,7 +606,7 @@ There are two scenarios in which you can use the `delete_account` action:
2. To make your smart contract delete its own account.
-
+
```rust
use near_sdk::{near, env, Promise, NearToken, AccountId};
@@ -838,9 +634,8 @@ There are two scenarios in which you can use the `delete_account` action:
}
```
-
-
-
+
+
```js
import { NearBindgen, near, call, NearPromise } from 'near-sdk-js'
@@ -868,9 +663,8 @@ There are two scenarios in which you can use the `delete_account` action:
}
```
-
-
-
+
+
```python
from near_sdk_py import call, Contract
@@ -901,51 +695,12 @@ class ExampleContract(Contract):
.delete_account(beneficiary)
```
-
-
-
-```go
-package main
-
-import (
- "github.com/vlmoon99/near-sdk-go/env"
- "github.com/vlmoon99/near-sdk-go/promise"
- "github.com/vlmoon99/near-sdk-go/types"
-)
-
-// @contract:state
-type Contract struct{}
-
-type DeleteAccountInput struct {
- Prefix string `json:"prefix"`
- Beneficiary string `json:"beneficiary"`
-}
-
-type SelfDeleteInput struct {
- Beneficiary string `json:"beneficiary"`
-}
-
-// @contract:payable min_deposit=0.001NEAR
-func (c *Contract) ExampleCreateDeleteAccount(input DeleteAccountInput) {
- currentAccountId, _ := env.GetCurrentAccountId()
- subaccountId := input.Prefix + "." + currentAccountId
- amount, _ := types.U128FromString("1000000000000000000000") // 0.001Ⓝ
-
- promise.CreateBatch(subaccountId).
- CreateAccount().
- Transfer(amount).
- DeleteAccount(input.Beneficiary)
-}
-
-// @contract:mutating
-func (c *Contract) ExampleSelfDeleteAccount(input SelfDeleteInput) {
- currentAccountId, _ := env.GetCurrentAccountId()
-
- promise.CreateBatch(currentAccountId).
- DeleteAccount(input.Beneficiary)
-}
-```
-
+
+
+
+
diff --git a/smart-contracts/anatomy/anatomy.mdx b/smart-contracts/anatomy/anatomy.mdx
index 1bf952b4f20..e4416ddb022 100644
--- a/smart-contracts/anatomy/anatomy.mdx
+++ b/smart-contracts/anatomy/anatomy.mdx
@@ -10,7 +10,7 @@ Let's illustrate the basic anatomy of a simple "Hello World" contract. The code
-
+
### Importing the SDK
All contracts will import the **NEAR SDK**, enabling them to [access the execution environment](./environment), [call other contracts](./crosscontract), [transfer tokens](./actions), and much more.
@@ -19,7 +19,7 @@ Let's illustrate the basic anatomy of a simple "Hello World" contract. The code
-
+
### Contract's Main Structure
The contract is described through a structure:
@@ -28,25 +28,15 @@ Let's illustrate the basic anatomy of a simple "Hello World" contract. The code
-
+
### Comment Directives
Unlike languages with built-in decorators, the Near Go SDK uses **Comment Directives** to control how your code is compiled into a smart contract.
- The generator (built into the `near-go` CLI) scans your comments for specific tags starting with `@contract:`.
-
- > **Note:** These directives rely on the `near-go` build process and will not work if you compile using raw `tinygo`.
+ The `near-go` CLI scans your comments for `@contract:` tags — such as `@contract:state`, `@contract:init`, `@contract:view`, `@contract:mutating`, `@contract:payable`, and `@contract:promise_callback` — and generates the contract bindings automatically.
- 1. **`@contract:state`**: Placed above a `struct`. It identifies the main state of the smart contract. **Only one state struct is allowed per project.** The CLI automatically generates `getState` and `setState` helper methods.
- 2. **`@contract:init`**: Marks the initialization method (constructor). It checks if the state is empty before running. **Only one init method is allowed per project.**
- 3. **`@contract:view`**: Marks a method as read-only. It exposes the method to the outside world but does not save changes to the state. Compatible with `@contract:promise_callback`.
- 4. **`@contract:mutating`**: Marks a method that modifies the state. The CLI automatically saves the updated state back to storage after execution. Required for callbacks that need to modify data.
- 5. **`@contract:payable`**: Allows the method to accept attached NEAR tokens. You can optionally specify requirements like `min_deposit=1NEAR`. Compatible with `mutating` and `init`.
- 6. **`@contract:promise_callback`**: Handles the result of a cross-contract call by injecting `promise.PromiseResult` into the arguments. **Must be combined** with either `@contract:view` (for read-only logic) or `@contract:mutating` (to save state changes).
-
-
- **Note:** Methods exported to WASM will automatically be converted to `snake_case` (e.g., `SetGreeting` becomes `set_greeting`).
+ **Note:** Methods exported to WASM are automatically converted to `snake_case` (e.g., `SetGreeting` becomes `set_greeting`).
@@ -104,7 +94,7 @@ Let's illustrate the basic anatomy of a simple "Hello World" contract. The code
-
+
### Storage (State)
We call the data stored in the contract [the contract's state](./storage).
@@ -130,7 +120,7 @@ Let's illustrate the basic anatomy of a simple "Hello World" contract. The code
-
+
### Read Only Functions
Contract's functions can be read-only, meaning they don't modify the state. Calling them is free for everyone, and does not require to have a NEAR account.
@@ -139,7 +129,7 @@ Let's illustrate the basic anatomy of a simple "Hello World" contract. The code
-
+
### State Mutating Functions
Functions that modify the state or call other contracts are considered state mutating functions. It is necessary to have a NEAR account to call them, as they require a transaction to be sent to the network.
@@ -157,5 +147,5 @@ Let's illustrate the basic anatomy of a simple "Hello World" contract. The code
start="2" end="32" />
-
+
diff --git a/smart-contracts/anatomy/best-practices.mdx b/smart-contracts/anatomy/best-practices.mdx
index b8153ddab62..f40836ca8c5 100644
--- a/smart-contracts/anatomy/best-practices.mdx
+++ b/smart-contracts/anatomy/best-practices.mdx
@@ -317,5 +317,170 @@ class Contract:
# ...
```
+
+
+
+Here we lay out some best practices for writing smart contracts in Go on NEAR:
+
+- [Validate early](#validate-early-go)
+- [Use logging](#use-logging-go)
+- [Return Promises](#return-promises-go)
+- [Use SDK collections for large data](#use-sdk-collections-go)
+- [Follow security patterns](#follow-security-patterns-go)
+
+---
+
+## Validate early {#validate-early-go}
+
+Validate inputs, context, and state as early as possible before taking any actions. The earlier you panic, the more [gas](/protocol/gas) you save for the caller.
+
+```go
+// @contract:mutating
+func (c *Contract) SetFee(newFee uint64) error {
+ caller, err := env.GetPredecessorAccountID()
+ if err != nil {
+ return err
+ }
+
+ // Validate permissions early
+ if caller != c.OwnerId {
+ env.PanicStr("Only the owner can set the fee")
+ }
+
+ // Validate input early
+ if newFee > 10000 {
+ env.PanicStr("Fee cannot exceed 10000 basis points")
+ }
+
+ // Only proceed after all checks pass
+ c.Fee = newFee
+ return nil
+}
+```
+
+---
+
+## Use logging {#use-logging-go}
+
+Use `env.LogString` for debug information and to notify users of important events. Log messages are stored on-chain and visible in transaction receipts.
+
+```go
+// @contract:mutating
+func (c *Contract) Transfer(to string, amount string) error {
+ caller, _ := env.GetPredecessorAccountID()
+
+ // Log the transfer for indexers and frontends
+ // Avoid "fmt" in TinyGo — use string concatenation instead
+ env.LogString("Transferring " + amount + " yoctoNEAR from " + caller + " to " + to)
+
+ transferAmount, err := types.U128FromString(amount)
+ if err != nil {
+ return err
+ }
+
+ promise.CreateBatch(to).Transfer(transferAmount)
+ return nil
+}
+```
+
+---
+
+## Return Promises {#return-promises-go}
+
+When making cross-contract calls, use `.Value()` to return the promise result to the caller. This lets the caller (e.g. near-cli or near-api-js) wait for the full result and see failures in the transaction chain.
+
+```go
+// @contract:payable min_deposit=0.00001NEAR
+func (c *Contract) WithdrawAndNotify(receiverId string) {
+ gas := uint64(10 * types.ONE_TERA_GAS)
+
+ // Return the promise chain — the caller will see the final result
+ promise.NewCrossContract(receiverId).
+ Gas(gas).
+ Call("on_deposit_received", map[string]string{}).
+ Value()
+}
+```
+
+---
+
+## Use SDK collections for large data {#use-sdk-collections-go}
+
+For data sets that grow over time, use SDK collections instead of native Go slices or maps. Native collections are fully loaded into memory on every call, which costs more gas as they grow.
+
+```go
+import "github.com/vlmoon99/near-sdk-go/collections"
+
+// Good: SDK collections load entries lazily
+// @contract:state
+type TokenContract struct {
+ Balances *collections.LookupMap[string, string] `json:"balances"`
+ Owners *collections.UnorderedSet[string] `json:"owners"`
+}
+
+// @contract:init
+func (c *TokenContract) Init() {
+ c.Balances = collections.NewLookupMap[string, string]("b")
+ c.Owners = collections.NewUnorderedSet[string]("o")
+}
+```
+
+
+
+Use native Go slices and maps only for **small, fixed-size** data (up to ~100 entries) that is always read together. For anything larger, use SDK collections.
+
+
+
+---
+
+## Follow security patterns {#follow-security-patterns-go}
+
+Apply security best practices to protect your contract from common vulnerabilities.
+
+```go
+// @contract:payable min_deposit=0.01NEAR
+// @contract:mutating
+func (c *Contract) Deposit() error {
+ caller, err := env.GetPredecessorAccountID()
+ if err != nil {
+ return err
+ }
+
+ deposit, err := env.GetAttachedDeposit()
+ if err != nil {
+ return err
+ }
+
+ // Re-entrancy protection: update state BEFORE any external calls
+ current, _ := c.Balances.Get(caller)
+ currentAmount, _ := types.U128FromString(current)
+ newAmount, _ := currentAmount.Add(deposit)
+ c.Balances.Insert(caller, newAmount.String())
+
+ // Only after state is updated, perform external calls if needed
+ env.LogString("Deposit recorded for " + caller)
+ return nil
+}
+
+// Access control: only the contract itself can call this callback
+// @contract:view
+// @contract:promise_callback
+func (c *Contract) OnExternalCallDone(result promise.PromiseResult) {
+ currentAccount, _ := env.GetCurrentAccountId()
+ caller, _ := env.GetPredecessorAccountID()
+
+ if caller != currentAccount {
+ env.PanicStr("Callbacks can only be called by the contract itself")
+ }
+
+ if !result.Success {
+ env.LogString("External call failed — rolling back state changes")
+ // manually revert any state changes made before the cross-contract call
+ }
+}
+```
+
+
+
diff --git a/smart-contracts/anatomy/collections.mdx b/smart-contracts/anatomy/collections.mdx
index 155e89e1ab2..9410784bf40 100644
--- a/smart-contracts/anatomy/collections.mdx
+++ b/smart-contracts/anatomy/collections.mdx
@@ -214,6 +214,12 @@ class MyContract:
````
+
+
+
+
@@ -248,13 +254,13 @@ class VectorExample:
def new(self):
# Create a Vector with prefix "v"
self.my_vector = Vector("v")
-
+
@call
def add_number(self, number):
# Append a value to the vector
self.my_vector.append(number)
return len(self.my_vector)
-
+
@view
def get_number(self, index):
# Get a value at specific index
@@ -262,13 +268,20 @@ class VectorExample:
return self.my_vector[index]
except Exception:
return None
-
+
@view
def get_all_numbers(self):
# Convert entire vector to a list
return [num for num in self.my_vector]
```
+
+
+
+
+
@@ -297,18 +310,18 @@ class LookupMapExample:
def new(self):
# Create a LookupMap with prefix "m"
self.balances = LookupMap("m")
-
+
@call
def set_balance(self, account_id, amount):
# Set a value for a key
self.balances[account_id] = amount
return True
-
+
@view
def get_balance(self, account_id):
# Get a value for a key with a default
return self.balances.get(account_id, 0)
-
+
@call
def remove_balance(self, account_id):
# Remove a key
@@ -318,6 +331,13 @@ class LookupMapExample:
return False
```
+
+
+
+
+
@@ -346,13 +366,13 @@ class UnorderedMapExample:
def new(self):
# Create an UnorderedMap with prefix "um"
self.user_data = UnorderedMap("um")
-
+
@call
def set_user_data(self, account_id, data):
# Set a value for a key
self.user_data[account_id] = data
return True
-
+
@view
def get_user_data(self, account_id):
# Get a value for a key
@@ -360,7 +380,7 @@ class UnorderedMapExample:
return self.user_data[account_id]
except Exception:
return None
-
+
@view
def list_all_users(self):
# Iterate through keys and values
@@ -371,6 +391,13 @@ class UnorderedMapExample:
}
```
+
+
+
+
+
@@ -401,18 +428,18 @@ class LookupSetExample:
def new(self):
# Create a LookupSet with prefix "s"
self.whitelist = LookupSet("s")
-
+
@call
def add_to_whitelist(self, account_id):
# Add a value to the set
self.whitelist.add(account_id)
return True
-
+
@view
def is_whitelisted(self, account_id):
# Check if a value exists in the set
return account_id in self.whitelist
-
+
@call
def remove_from_whitelist(self, account_id):
# Remove a value from the set
@@ -424,6 +451,12 @@ class LookupSetExample:
```
+
+
+
+
@@ -452,23 +485,23 @@ class UnorderedSetExample:
def new(self):
# Create an UnorderedSet with prefix "us"
self.owners = UnorderedSet("us")
-
+
@call
def register_owner(self, account_id):
# Add a value to the set
self.owners.add(account_id)
return True
-
+
@view
def is_owner(self, account_id):
# Check if a value exists in the set
return account_id in self.owners
-
+
@view
def list_all_owners(self):
# Get all values as a list
return self.owners.values()
-
+
@call
def remove_owner(self, account_id):
# Try to remove a value if it exists
@@ -476,6 +509,13 @@ class UnorderedSetExample:
return True
```
+
+
+
+
+
@@ -499,33 +539,39 @@ class TreeMapExample:
def new(self):
# Create a TreeMap with prefix "tm"
self.scores = TreeMap("tm")
-
+
@call
def add_score(self, user_id, score):
# Set score for a user
self.scores[user_id] = score
return True
-
+
@view
def get_top_scores(self, limit=10):
# Get top scores using ordered keys
- # This returns highest scores first
top_users = []
max_key = self.scores.max_key()
current_key = max_key
count = 0
-
+
while current_key is not None and count < limit:
top_users.append({
"user": current_key,
"score": self.scores[current_key]
})
- current_key = self.scores.floor_key(current_key - 1) # Get next highest key
+ current_key = self.scores.floor_key(current_key - 1)
count += 1
-
+
return top_users
```
+
+
+
+
+
---
@@ -601,6 +647,19 @@ class NestedCollectionsExample:
In Python, we create unique prefixes for nested collections by including the parent's identifier in the prefix string. The SDK also provides a `create_prefix_guard` utility to help manage prefixes.
+
+
+
+
+
+
+
+
+
+In Go, nested `Vector` collections must have their `Len` persisted separately (e.g., in a `LookupMap[string, uint64]`). When reconstructing a vector, pass the saved length via `&collections.Vector[string]{Prefix: prefix, Len: savedLen}`. Always use unique prefixes across all collections.
+
@@ -684,6 +743,12 @@ assert!(!m.contains(&1));
```
+
+
+
+
---
@@ -742,23 +807,19 @@ In order to expose them all through view calls, we can use pagination.
@view
def get_updates(self, from_index: int = 0, limit: int = 50) -> list:
- """Returns multiple elements from the collection with pagination.
-
- Args:
- from_index: The index to start from
- limit: The maximum number of elements to return
-
- Returns:
- A list of elements from the collection
- """
- # Get all values from the collection
+ """Returns multiple elements from the collection with pagination."""
all_items = self.status_updates.values()
-
- # Apply pagination with list slicing
start = min(from_index, len(all_items))
end = min(start + limit, len(all_items))
-
return all_items[start:end]
```
+
+
+ With Go this can be done using index-based iteration over a `Vector`, loading only the elements within the requested range.
+
+
+
diff --git a/smart-contracts/anatomy/crosscontract.mdx b/smart-contracts/anatomy/crosscontract.mdx
index 5d70daa75dd..552aeeb9eb6 100644
--- a/smart-contracts/anatomy/crosscontract.mdx
+++ b/smart-contracts/anatomy/crosscontract.mdx
@@ -101,58 +101,9 @@ class CrossContractExample(Contract):
-```go
-package main
-
-import (
- "github.com/vlmoon99/near-sdk-go/env"
- "github.com/vlmoon99/near-sdk-go/promise"
- "github.com/vlmoon99/near-sdk-go/types"
-)
-
-// @contract:state
-type Contract struct{}
-
-// @contract:payable min_deposit=0.001NEAR
-func (c *Contract) ExampleQueryingGreetingInfo() {
- helloAccount := "hello-nearverse.testnet"
- gas := uint64(10 * types.ONE_TERA_GAS)
-
- promise.NewCrossContract(helloAccount).
- Gas(gas).
- Call("get_greeting", map[string]string{}).
- Value()
-}
-
-// @contract:payable min_deposit=0.001NEAR
-func (c *Contract) ExampleQueryingInformation() {
- helloAccount := "hello-nearverse.testnet"
- gas := uint64(10 * types.ONE_TERA_GAS)
-
- promise.NewCrossContract(helloAccount).
- Gas(gas).
- Call("get_greeting", map[string]string{}).
- Then("example_querying_information_response", map[string]string{})
-}
-
-// @contract:view
-// @contract:promise_callback
-func (c *Contract) ExampleQueryingInformationResponse(result promise.PromiseResult) {
-
- if result.Success {
- env.LogString("State change/Query completed successfully")
- } else {
- env.LogString("State change/Query failed")
- }
-
- env.LogString("Promise result status: " + types.IntToString(result.StatusCode))
- if len(result.Data) > 0 {
- env.LogString("Returned data: " + string(result.Data))
- } else {
- env.LogString("No return data")
- }
-}
-```
+
@@ -230,50 +181,9 @@ class CrossContractExample(Contract):
-```go
-package main
-
-import (
- "github.com/vlmoon99/near-sdk-go/env"
- "github.com/vlmoon99/near-sdk-go/promise"
- "github.com/vlmoon99/near-sdk-go/types"
-)
-
-// @contract:state
-type Contract struct{}
-
-// @contract:payable min_deposit=0.00001NEAR
-func (c *Contract) ExampleSendingInformation() {
- helloAccount := "hello-nearverse.testnet"
- gas := uint64(30 * types.ONE_TERA_GAS)
-
- args := map[string]string{
- "message": "New Greeting",
- }
-
- promise.NewCrossContract(helloAccount).
- Gas(gas).
- Call("set_greeting", args).
- Then("example_change_greeting_callback", map[string]string{})
-}
-
-// @contract:view
-// @contract:promise_callback
-func (c *Contract) ExampleChangeGreetingCallback(result promise.PromiseResult) {
- if result.Success {
- env.LogString("State change completed successfully")
- } else {
- env.LogString("State change failed")
- }
-
- env.LogString("Promise result status: " + types.IntToString(int(result.StatusCode)))
- if len(result.Data) > 0 {
- env.LogString("Returned data: " + string(result.Data))
- } else {
- env.LogString("No return data from state change")
- }
-}
-```
+
@@ -366,55 +276,13 @@ To create a cross-contract call with a callback, create two promises and use the
-
- ```go
- package main
-
- import (
- "github.com/vlmoon99/near-sdk-go/env"
- "github.com/vlmoon99/near-sdk-go/promise"
- "github.com/vlmoon99/near-sdk-go/types"
- )
-
- // @contract:state
- type Contract struct{}
-
- type PromiseCallbackInputData struct {
- Data string `json:"data"`
- }
-
- // @contract:payable min_deposit=0.00001NEAR
- func (c *Contract) ExampleCrossContractCall() {
- externalAccount := "hello-nearverse.testnet"
- gas := uint64(5 * types.ONE_TERA_GAS)
-
- args := map[string]string{
- "message": "New Greeting",
- }
- callback_args := map[string]string{
- "data": "saved_for_callback",
- }
- promise.NewCrossContract(externalAccount).
- Gas(gas).
- Call("set_greeting", args).
- Then("example_cross_contract_callback", callback_args).
- Value()
- }
-
- // @contract:view
- // @contract:promise_callback
- func (c *Contract) ExampleCrossContractCallback(input PromiseCallbackInputData, result promise.PromiseResult) {
- env.LogString("Executing callback")
-
- env.LogString("Input CrossContractCallback : " + input.Data)
-
- if result.Success {
- env.LogString("Cross-contract call executed successfully")
- } else {
- env.LogString("Cross-contract call failed")
- }
- }
- ```
+
+
+
+In Go, `.Then()` works differently from Rust/JS. Instead of receiving a second promise object, it takes the callback method name as a **snake_case string** — the SDK automatically routes the callback to the current contract. You do not construct a second promise manually.
+
@@ -503,25 +371,9 @@ class CrossContractExample(Contract):
-```go
-type PromiseCallbackInputData struct {
- Data string `json:"data"`
-}
-
-// @contract:view
-// @contract:promise_callback
-func (c *Contract) ExampleCrossContractCallback(input PromiseCallbackInputData, result promise.PromiseResult) {
- env.LogString("Executing callback")
-
- env.LogString("Input CrossContractCallback : " + input.Data)
-
- if result.Success {
- env.LogString("Cross-contract call executed successfully")
- } else {
- env.LogString("Cross-contract call failed")
- }
-}
-```
+
@@ -629,62 +481,10 @@ class BatchCallsExample(Contract):
-
-```go
-package main
-
-import (
- "strconv"
-
- "github.com/vlmoon99/near-sdk-go/env"
- "github.com/vlmoon99/near-sdk-go/promise"
- "github.com/vlmoon99/near-sdk-go/types"
-)
-
-// @contract:state
-type Contract struct{}
-
-type PromiseCallbackInputData struct {
- Data string `json:"data"`
-}
-
-// @contract:payable min_deposit=0.00001NEAR
-func (c *Contract) ExampleBatchCallsSameContract() {
- helloAccount := "hello-nearverse.testnet"
- gas := uint64(10 * types.ONE_TERA_GAS)
- amount, _ := types.U128FromString("0")
- callback_args := map[string]string{
- "data": "[Greeting One, Greeting Two]",
- }
-
- promise.NewCrossContract(helloAccount).
- Batch().
- Gas(gas).
- FunctionCall("set_greeting", map[string]string{
- "message": "Greeting One",
- }, amount, gas).
- FunctionCall("another_method", map[string]string{
- "arg1": "val1",
- }, amount, gas).
- Then(helloAccount).
- FunctionCall("example_batch_calls_callback", callback_args, amount, gas)
-
- env.LogString("Batch call created successfully")
-}
-
-// @contract:view
-// @contract:promise_callback
-func (c *Contract) ExampleBatchCallsCallback(input PromiseCallbackInputData, result promise.PromiseResult) {
- env.LogString("Processing batch call results")
- env.LogString("Input CrossContractCallback : " + input.Data)
-
- env.LogString("Batch call success: " + strconv.FormatBool(result.Success))
- if len(result.Data) > 0 {
- env.LogString("Batch call data: " + string(result.Data))
- }
-}
-```
-
+
+
@@ -768,61 +568,10 @@ class MultiContractExample(Contract):
-
-```go
-package main
-
-import (
- "strconv"
-
- "github.com/vlmoon99/near-sdk-go/env"
- "github.com/vlmoon99/near-sdk-go/promise"
- "github.com/vlmoon99/near-sdk-go/types"
-)
-
-// @contract:state
-type Contract struct{}
-
-type PromiseCallbackInputData struct {
- Data string `json:"data"`
-}
-
-// @contract:payable min_deposit=0.00001NEAR
-func (c *Contract) ExampleParallelCallsDifferentContracts() {
- contractA := "hello-nearverse.testnet"
- contractB := "child.neargopromises1.testnet"
-
- promiseA := promise.NewCrossContract(contractA).
- Call("get_greeting", map[string]string{})
-
- promiseB := promise.NewCrossContract(contractB).
- Call("SetStatus", map[string]string{"message": "Hello, World!"})
-
- promiseA.Join([]*promise.Promise{promiseB}, "example_parallel_contracts_callback", map[string]string{
- "data": contractA + "," + contractB,
- }).Value()
-
- env.LogString("Parallel contract calls initialized")
-}
-
-// @contract:view
-// @contract:promise_callback
-func (c *Contract) ExampleParallelContractsCallback(input PromiseCallbackInputData, results []promise.PromiseResult) {
- env.LogString("Processing results from multiple contracts")
- env.LogString("Input CrossContractCallback : " + input.Data)
-
- for i, result := range results {
- env.LogString("Processing result " + types.IntToString(i))
- env.LogString("Success: " + strconv.FormatBool(result.Success))
- if len(result.Data) > 0 {
- env.LogString("Data: " + string(result.Data))
- }
- }
-
- env.LogString("Processed " + types.IntToString(len(results)) + " contract responses")
-}
-```
-
+
+
@@ -850,4 +599,4 @@ We have a whole [security section](../security/callbacks) dedicated to these spe
Not following these basic security guidelines could expose your contract to exploits. Please check the [security section](../security/callbacks), and if still in doubt, [join us in Discord](https://near.chat).
-
\ No newline at end of file
+
diff --git a/smart-contracts/anatomy/environment.mdx b/smart-contracts/anatomy/environment.mdx
index 32c0bcb35e9..27b22abf349 100644
--- a/smart-contracts/anatomy/environment.mdx
+++ b/smart-contracts/anatomy/environment.mdx
@@ -219,6 +219,21 @@ def check_gas(required_gas=20_000_000_000_000): # 20 TGas
+
+
+```go
+const REQUIRED_GAS = uint64(20_000_000_000_000) // 20 TGas
+
+// @contract:mutating
+func (c *MyContract) CheckGas() {
+ if env.GetPrepaidGas() < REQUIRED_GAS {
+ env.PanicStr("Please attach at least 20 TGas")
+ }
+}
+```
+
+
+
@@ -282,12 +297,10 @@ Besides environmental variables, the SDK also exposes some functions to perform
| Function Name | SDK method | Description |
|-----------------------|---------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| SHA 256 | `env.Sha256Hash(value)` | Hashes a sequence of bytes using sha256. |
-| Keccak 256 | `env.Keccak256Hash(value)` | Hashes a sequence of bytes using keccak256. |
-| Keccak 512 | `env.Keccak512Hash(value)` | Hashes a sequence of bytes using keccak512. |
-| SHA 256 | `env::Sha256Hash(value)` | Hashes the bytes using the SHA-256 hash function. This returns a 32 byte hash.
- |
-| RIPEMD 160 | `env::Ripemd160Hash(value)` | Hashes the bytes using the RIPEMD-160 hash function. This returns a 20 byte hash. |
+| SHA 256 | `env.Sha256Hash(value)` | Hashes a sequence of bytes using sha256. |
+| Keccak 256 | `env.Keccak256Hash(value)` | Hashes a sequence of bytes using keccak256. |
+| Keccak 512 | `env.Keccak512Hash(value)` | Hashes a sequence of bytes using keccak512. |
+| RIPEMD 160 | `env.Ripemd160Hash(value)` | Hashes the bytes using the RIPEMD-160 hash function. This returns a 20 byte hash. |
| EC Recover | `env.EcrecoverPubKey(hash, signature, v, malleability_flag)` | Recovers an ECDSA signer address from a 32-byte message `hash` and a corresponding `signature` along with `v` recovery byte. Takes in an additional flag to check for malleability of the signature which is generally only ideal for transactions. Returns 64 bytes representing the public key if the recovery was successful. |
| Panic String | `env.PanicStr(message)` | Terminates the execution of the program with the UTF-8 encoded message. |
| Log String | `env.LogString(message)` | Logs the string message. This message is stored on chain. |
diff --git a/smart-contracts/anatomy/functions.mdx b/smart-contracts/anatomy/functions.mdx
index eb61e72ab93..b8f7888f446 100644
--- a/smart-contracts/anatomy/functions.mdx
+++ b/smart-contracts/anatomy/functions.mdx
@@ -9,9 +9,9 @@ import { ExplainCode, Block , File } from '/snippets/explain-code.jsx'
Smart contracts expose functions so users can interact with them. There are different types of functions including `read-only`, `private` and `payable`.
-
+
-
+
### Contract's Interface
@@ -42,7 +42,7 @@ impl MyTrait for MyContractStructure {
-
+
### Initialization Functions
@@ -74,7 +74,15 @@ The initialization function is marked with the `@init` decorator in Python.
-
+
+
+#### `@contract:init`
+
+The initialization function is marked with the `@contract:init` comment directive in Go.
+
+
+
+
### State Changing Functions
@@ -106,7 +114,15 @@ State changing functions are marked with the `@call` decorator in Python.
-
+
+
+#### `@contract:mutating`
+
+State changing functions are marked with the `@contract:mutating` comment directive in Go.
+
+
+
+
@@ -116,7 +132,7 @@ The SDK provides [contextual information](./environment), such as which account
-
+
### Read-Only Functions
@@ -148,6 +164,14 @@ Read-only functions are marked with the `@view` decorator in Python.
+
+
+#### `@contract:view`
+
+Read-only functions are marked with the `@contract:view` comment directive in Go.
+
+
+
### Private Functions
@@ -184,7 +208,7 @@ In Python, you can create callbacks by using the `@callback` decorator, which is
-
+
### Payable Functions
@@ -218,6 +242,14 @@ In Python, you need to check the deposit manually using the Context API. There i
+
+
+#### `@contract:payable`
+
+In Go, functions that accept attached NEAR tokens can be marked with the `@contract:payable` comment directive. You can optionally specify a minimum deposit requirement: `@contract:payable min_deposit=1NEAR`. This directive is compatible with `@contract:mutating` and `@contract:init`.
+
+
+
### Internal Functions
@@ -254,6 +286,18 @@ To create internal private methods in a Python contract, simply define normal me
+
+
+### Internal Functions
+
+All the functions we covered so far are part of the interface, meaning they can be called by an external actor.
+
+However, contracts can also have private internal functions - such as helper or utility functions - that are **not exposed** to the outside world.
+
+To create internal private methods in a Go contract, simply define regular methods without any `@contract:` comment directives.
+
+
+
@@ -295,6 +339,8 @@ They are useful to return hardcoded values on the contract.
+
+
```js
@@ -363,4 +409,34 @@ class Contract:
+
+
+```go
+package main
+
+const SOME_VALUE uint64 = 8
+
+// @contract:state
+type MyContract struct {
+ Counter uint64 `json:"counter"`
+}
+
+// Internal helper — no @contract: directive, not exposed to the outside
+func (c *MyContract) doubleValue(n uint64) uint64 {
+ return n * 2
+}
+
+// @contract:view
+func (c *MyContract) GetStaticValue() uint64 {
+ return SOME_VALUE
+}
+
+// @contract:view
+func (c *MyContract) GetDoubledCounter() uint64 {
+ return c.doubleValue(c.Counter)
+}
+```
+
+
+
diff --git a/smart-contracts/anatomy/reduce-size.mdx b/smart-contracts/anatomy/reduce-size.mdx
index 41ce6f7c541..3f92138678b 100644
--- a/smart-contracts/anatomy/reduce-size.mdx
+++ b/smart-contracts/anatomy/reduce-size.mdx
@@ -310,5 +310,118 @@ def frequently_called_method(self, data):
pass
```
+
+
+
+## Advice & examples
+
+Go contracts are compiled to WASM via **TinyGo**, which already applies aggressive size optimizations compared to the standard Go compiler. The `near-go build` command uses size-optimized TinyGo settings by default.
+
+Below are additional strategies to further reduce your contract size.
+
+---
+
+## Use `near-go build` (default)
+
+Always build with `near-go build` instead of raw `tinygo`. It applies the correct flags for NEAR and enables size optimizations automatically:
+
+```bash
+near-go build
+```
+
+To inspect the intermediate generated code or set a custom output name:
+
+```bash
+near-go build --output my_contract.wasm --keep-generated
+```
+
+---
+
+## Minimize struct fields
+
+Every field stored in the contract state is serialized to JSON. Keep your state structs lean — only store what you need on-chain.
+
+```go
+// Avoid: storing redundant or derived data
+// @contract:state
+type BadContract struct {
+ Balance string `json:"balance"`
+ BalanceStr string `json:"balance_str"` // redundant — derive it when needed
+ LastCaller string `json:"last_caller"`
+ CallCount uint64 `json:"call_count"` // may not be necessary
+}
+
+// Better: store only essential state
+// @contract:state
+type GoodContract struct {
+ Balance string `json:"balance"`
+}
+```
+
+---
+
+## Avoid large dependencies
+
+TinyGo has a limited standard library. Avoid importing large packages that bring in code not needed at runtime. Prefer the SDK's built-in utilities:
+
+```go
+// Avoid: importing "fmt" for formatting — it adds significant size
+import "fmt"
+env.LogString(fmt.Sprintf("value: %d", x))
+
+// Better: use strconv or SDK helpers for simple conversions
+import "strconv"
+env.LogString("value: " + strconv.FormatUint(x, 10))
+```
+
+---
+
+## Use SDK collections for large data
+
+Native Go slices and maps are fully serialized into the `STATE` key on every call, making the contract state grow with the collection. Use SDK collections to store entries as separate storage keys:
+
+```go
+import "github.com/vlmoon99/near-sdk-go/collections"
+
+// Avoid for large datasets: native map is fully loaded on every call
+// @contract:state
+type BadContract struct {
+ Balances map[string]string `json:"balances"`
+}
+
+// Better: SDK LookupMap only loads entries on demand
+// @contract:state
+type GoodContract struct {
+ Balances *collections.LookupMap[string, string] `json:"balances"`
+}
+```
+
+---
+
+## Panic early, before doing expensive work
+
+Failing fast with `env.PanicStr` saves gas and avoids wasted computation. Validate all inputs and permissions before executing any logic:
+
+```go
+// @contract:mutating
+func (c *Contract) AdminAction(data string) {
+ caller, _ := env.GetPredecessorAccountID()
+
+ // Validate permissions immediately
+ if caller != c.OwnerId {
+ env.PanicStr("Only the owner can call this method")
+ }
+
+ // Validate input before processing
+ if len(data) == 0 {
+ env.PanicStr("Data must not be empty")
+ }
+
+ // ... proceed with the actual logic
+}
+```
+
+
+
diff --git a/smart-contracts/anatomy/reproducible-builds.mdx b/smart-contracts/anatomy/reproducible-builds.mdx
index 7fb775786df..9201fdfb73d 100644
--- a/smart-contracts/anatomy/reproducible-builds.mdx
+++ b/smart-contracts/anatomy/reproducible-builds.mdx
@@ -105,4 +105,39 @@ To ensure your Python contracts are reproducible:
4. **Avoid system-specific code**: Don't rely on features that might behave differently across operating systems.
+
+
+## Go Contracts and Reproducible Builds
+
+Go contracts compiled with `near-go build` use **TinyGo** under the hood. Build outputs can vary between TinyGo versions and operating systems, similar to Rust before `cargo-near`.
+
+
+
+Official tooling for reproducible Go contract builds (equivalent to `cargo-near` + Docker for Rust) is not yet available. This feature is planned for a future release of `near-go`.
+
+
+
+### Best practices in the meantime
+
+To maximize the chance of consistent builds across environments:
+
+1. **Pin your `near-go` version**: Install a specific version of `near-go` and document it in your project's README.
+
+ ```bash
+ # Check your near-go version
+ near-go --version
+ ```
+
+2. **Pin your `near-sdk-go` version**: Use a fixed version in your `go.mod` rather than a floating tag.
+
+ ```go
+ // go.mod
+ require github.com/vlmoon99/near-sdk-go v0.1.1
+ ```
+
+3. **Use CI for builds**: Build your WASM in a controlled CI environment (e.g. GitHub Actions) using a pinned `near-go` version. Publish the resulting WASM as a release artifact alongside your source code.
+
+4. **Provide the source**: Publish your contract's source code on a public repository so users can verify the logic independently, even before binary verification tooling is available.
+
+
\ No newline at end of file
diff --git a/smart-contracts/anatomy/serialization.mdx b/smart-contracts/anatomy/serialization.mdx
index dfa7d38dd45..8dafe32de45 100644
--- a/smart-contracts/anatomy/serialization.mdx
+++ b/smart-contracts/anatomy/serialization.mdx
@@ -114,6 +114,19 @@ The `NEAR SDK RS` currently implements the `near_sdk::json_types::{U64, I64, U12
that you can use for input / output of data.
+
+**Go: u64 as strings**
+The Go SDK follows the same rule. Use `string` for `uint64` values in function parameters and return types when they may exceed 53 bits. For 128-bit token amounts, use `types.Uint128` from the Go SDK.
+
+```go
+// Use string for large numbers passed as function arguments
+type MyInput struct {
+ Amount string `json:"amount"` // pass as "1000000000000000000000000"
+}
+```
+
+
+
---
## Borsh: State Serialization
@@ -130,6 +143,12 @@ The JavaScript SDK uses JSON to serialize objects in the state, but the borsh im
should arrive soon
+
+**Go SDK uses JSON for state**
+
+The Go SDK also uses JSON (not Borsh) to serialize the contract state. Struct fields must have `json:"field_name"` tags for correct serialization and deserialization.
+
+
#### Example
Let's look at this example, written only for educational purposes:
diff --git a/smart-contracts/anatomy/storage.mdx b/smart-contracts/anatomy/storage.mdx
index e74f2e30390..6fe2319160c 100644
--- a/smart-contracts/anatomy/storage.mdx
+++ b/smart-contracts/anatomy/storage.mdx
@@ -11,7 +11,7 @@ Smart contracts store data in their account's state, which is public on the chai
It is important to know that the account's **code** and account's **storage** are **independent**. [Updating the code](../release/upgrade) does **not erase** the state.
-
+
@@ -41,7 +41,18 @@ It is important to know that the account's **code** and account's **storage** ar
-
+
+
+ ### Defining the State
+ The attributes of the `struct` marked with `@contract:state` define the data that will be stored.
+
+ The contract can store all native types (e.g. `uint64`, `string`, `bool`) as well as complex objects, serialized as JSON.
+
+ For example, our Auction contract stores when the auction ends, and an object representing the highest bid.
+
+
+
+
@@ -53,7 +64,7 @@ It currently costs ~**1Ⓝ** to store **100KB** of data.
-
+
### Initializing the State
After the contract is deployed, its state is empty and needs to be initialized with
@@ -97,6 +108,17 @@ In Python, you need to manage the state initialization explicitly. The SDK doesn
+
+
+ ### I. Initialization Functions
+ An option to initialize the state is to create an `initialization` function, which needs to be called before executing any other function.
+
+ In our Auction example, the contract has an initialization function that sets when the auction ends. The function is marked with the `@contract:init` comment directive.
+
+ **Note:** It is a good practice to mark initialization functions as private. We will cover function types in the [functions section](./functions.md).
+
+
+
### II. Default State
@@ -123,18 +145,30 @@ In Python, you need to manage the state initialization explicitly. The SDK doesn
-
+
- ### Lifecycle of the State
- When a function is called, the contract's state is loaded from the storage and put into memory.
+ ### II. Default State
+ In Go, when no `@contract:init` function is called, the contract's state struct is zero-initialized using Go's default zero values — `""` for strings, `0` for numbers, `false` for booleans.
- The state is actually [stored serialized](./serialization), and the SDK takes a bit of time to deserialize it before the method can access it.
+ **Note:** The state can only be initialized once.
+
+
- When the method finishes executing successfully, all the changes to the state are serialized, and saved back to the storage.
+
+
+
+**Lifecycle of the State**
+
+When a function is called, the contract's state is loaded from the storage and put into memory.
+
+The state is actually [stored serialized](./serialization), and the SDK takes a bit of time to deserialize it before the method can access it.
+
+When the method finishes executing successfully, all the changes to the state are serialized, and saved back to the storage.
+
-
+
**State and Code**
@@ -169,4 +203,12 @@ Make sure to read the [updating a contract](../release/upgrade) if you run into
+
+
+
+
diff --git a/smart-contracts/anatomy/types.mdx b/smart-contracts/anatomy/types.mdx
index 821a6ba9674..80990590bfe 100644
--- a/smart-contracts/anatomy/types.mdx
+++ b/smart-contracts/anatomy/types.mdx
@@ -8,7 +8,7 @@ import { ExplainCode, Block , File } from '/snippets/explain-code.jsx'
Lets discuss which types smart contracts use to input and output data, as well as how such data is stored and handled in the contract's code.
-
+
@@ -53,6 +53,19 @@ Lets discuss which types smart contracts use to input and output data, as well a
+
+
+ ### Native Types
+ Smart contracts can receive, store and return data using the following Go types:
+ - `string`
+ - `uint64`, `int64`, `float64`
+ - `bool`
+ - Custom structs (with `json:"..."` tags for serialization)
+
+ **Note:** Go does not have a native 128-bit integer type. Use `types.Uint128` from the SDK to handle large token amounts.
+
+
+
@@ -77,7 +90,19 @@ Python's `int` type has unlimited precision, so it can handle large integers (li
-
+
+
+
+**`types.Uint128`**
+
+Go does not have a native 128-bit integer type. The SDK provides `types.Uint128` to handle large token amounts (e.g. yoctoNEAR values).
+
+Use `types.U128FromString(str)` to parse from a string, and `.String()` to convert back for output. Use `types.U64ToUint128(n)` for small constants.
+
+
+
+
+
### Complex Objects
Smart contracts can store and return complex objects
@@ -95,6 +120,14 @@ Python's `int` type has unlimited precision, so it can handle large integers (li
+
+
+ #### JSON Tags
+
+ In Go, struct fields used as input, output, or contract state must have `json:"field_name"` tags. The SDK uses these tags to serialize and deserialize data automatically.
+
+
+
#### Serialization
@@ -132,7 +165,21 @@ Python's `int` type has unlimited precision, so it can handle large integers (li
-
+
+
+ ### Handling Tokens
+ `$NEAR` tokens are represented as `types.Uint128` in Go, with values in `yoctoNEAR`.
+
+ - `env.GetAttachedDeposit()` returns the attached deposit as `types.Uint128`
+ - `types.U128FromString(str)` parses a token amount from string
+ - `types.U64ToUint128(n)` converts a small constant to `Uint128`
+ - `.String()` converts back to string for storage or output
+
+ **Note:** 1 NEAR = 10^24 yoctoNEAR
+
+
+
+
### Account
The SDK exposes a special type to handle NEAR Accounts, which automatically checks if the account address is valid
@@ -147,6 +194,13 @@ Python's `int` type has unlimited precision, so it can handle large integers (li
+
+
+ ### Account IDs
+ In Go, NEAR account IDs are represented as `string`. Functions like `env.GetPredecessorAccountID()` and `env.GetCurrentAccountId()` return account IDs as plain strings.
+
+
+
@@ -166,4 +220,12 @@ Python's `int` type has unlimited precision, so it can handle large integers (li
start="2" end="84" />
+
+
+
+
diff --git a/smart-contracts/anatomy/yield-resume.mdx b/smart-contracts/anatomy/yield-resume.mdx
index 33a85e1c7f2..3914f7afa7d 100644
--- a/smart-contracts/anatomy/yield-resume.mdx
+++ b/smart-contracts/anatomy/yield-resume.mdx
@@ -27,7 +27,14 @@ Let's look at an example that takes a prompt from a user (e.g. "What is 2+2"), a
url="https://github.com/near-examples/yield-resume/blob/main/contract/src/lib.rs"
start="43" end="70" />
-
+
+
+
+
+
+
```python
from near_sdk_py import call, view, near, Context
@@ -110,7 +117,14 @@ The `env::promise_yield_resume` function in Rust or `near.promise_yield_resume`
url="https://github.com/near-examples/yield-resume/blob/main/contract/src/lib.rs"
start="72" end="75" />
-
+
+
+
+
+
+
```python
@call
@@ -142,7 +156,14 @@ The function being resumed will have access to all parameters passed to it, incl
url="https://github.com/near-examples/yield-resume/blob/main/contract/src/lib.rs"
start="77" end="89" />
-
+
+
+
+
+
+
```python
@call
diff --git a/smart-contracts/quickstart.mdx b/smart-contracts/quickstart.mdx
index f28a93d38fa..103dc33fa97 100644
--- a/smart-contracts/quickstart.mdx
+++ b/smart-contracts/quickstart.mdx
@@ -105,7 +105,7 @@ curl -LsSf https://astral.sh/uv/install.sh | sh
# Install NEAR CLI-RS to deploy and interact with the contract
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/near/near-cli-rs/releases/latest/download/near-cli-rs-installer.sh | sh
-```text
+```
@@ -127,7 +127,6 @@ gvm install go1.25.4 -B
gvm use go1.25.4 --default
curl -LO https://github.com/vlmoon99/near-cli-go/releases/latest/download/install.sh && bash install.sh
-
```
@@ -178,6 +177,37 @@ In the meantime, please check out the [hello-near](https://github.com/near-examp
+
+
+```bash
+near-go create --project-name auction --module-name auction --project-type smart-contract-empty
+```
+
+
+_Creating a project using `near-go create`_
+
+This will generate a project with the following structure:
+
+```bash
+auction/
+├── go.mod # Go module definition with near-sdk-go dependency
+├── go.sum
+├── main.go # Main contract file — replace this with the auction contract below
+└── main_test.go # Unit tests
+```
+
+```bash
+cd auction
+```
+
+
+
+Replace the contents of `main.go` with the auction contract code shown in the sections below.
+
+
+
+
+
@@ -216,6 +246,12 @@ The contract stores the highest bid, auction end time, auctioneer address, and a
url="https://github.com/near-examples/auctions-tutorial/blob/main/contract-py/01-basic-auction/contract.py"
start="1" end="18" />
+
+
+
+
### Placing Bids
@@ -242,6 +278,12 @@ The `bid` function will then validate that the auction is ongoing, if the user b
url="https://github.com/near-examples/auctions-tutorial/blob/main/contract-py/01-basic-auction/contract.py"
start="20" end="45" />
+
+
+
+
### Claiming Proceeds
@@ -266,6 +308,12 @@ Once the auction ends, any user can call `claim` to transfer the winning bid to
url="https://github.com/near-examples/auctions-tutorial/blob/main/contract-py/01-basic-auction/contract.py"
start="47" end="59" />
+
+
+
+
### View Methods
@@ -290,6 +338,12 @@ At all times, users can query the current state of the auction by calling its vi
url="https://github.com/near-examples/auctions-tutorial/blob/main/contract-py/01-basic-auction/contract.py"
start="62" end="90" />
+
+
+
+
---
@@ -326,7 +380,17 @@ Lets make sure the contract is working as expected by running its tests. Simply
```bash
uv run pytest
```
-
+
+
+
+
+
+
+ ```bash
+ near-go test project
+ ```
+
+
@@ -370,7 +434,7 @@ Now that we know the tests are passing, let us deploy the contract! First, we ne
- :::important
+
This step requires [Emscripten](https://emscripten.org/) to be installed and accessible in your `PATH`. If you encounter errors during compilation, verify that Emscripten is properly installed with `emcc --version`.
@@ -379,7 +443,7 @@ Now that we know the tests are passing, let us deploy the contract! First, we ne
- `error: invalid version of emscripten` - Your Emscripten version might be too old. Try updating with `./emsdk install latest && ./emsdk activate latest`.
- `Could not find platform micropython-dev-wasm32` - This typically means the Emscripten installation is incomplete or not properly activated.
- :::
+
@@ -463,6 +527,15 @@ With the contract ready, we can now deploy it to the `testnet` account we create
near deploy ./auction.wasm
```
+
+
+
+
+ ```bash
+ near deploy ./main.wasm
+ ```
+
+
@@ -595,5 +668,8 @@ At the time of this writing, this example works with the following versions:
- near-sdk-py: `0.7.3`
- uvx nearc: `0.9.2`
- emscripten: `4.0.9` (required for Python contracts)
+- Go: `1.25.4`
+- near-go (near-cli-go): `v0.1.1`
+- near-sdk-go: `v0.1.1`
diff --git a/smart-contracts/release/deploy.mdx b/smart-contracts/release/deploy.mdx
index 2f98b1259a5..b8e51b3d9c3 100644
--- a/smart-contracts/release/deploy.mdx
+++ b/smart-contracts/release/deploy.mdx
@@ -41,6 +41,14 @@ Thanks to the `NEAR CLI` deploying a contract is as simple as:
+
+
+ ```bash
+ near-go build
+ ```
+
+
+
### Create an Account and Deploy
diff --git a/smart-contracts/release/upgrade.mdx b/smart-contracts/release/upgrade.mdx
index 57f15a237be..717fab1b7f8 100644
--- a/smart-contracts/release/upgrade.mdx
+++ b/smart-contracts/release/upgrade.mdx
@@ -48,6 +48,10 @@ A smart contract can also update itself by implementing a method that:
url="https://github.com/near-examples/update-migrate-rust/blob/main/self-updates/base/src/update.rs"
start="10" end="31" />
+
+
#### How to Invoke Such Method?
@@ -143,6 +147,11 @@ using the following state:
url="https://github.com/near/near-sdk-js/blob/develop/examples/src/basic-updates/basic-updates-base.js"
start="12" end="33" />
+
+
+
#### Update Contract
@@ -159,6 +168,11 @@ the `PostedMessage` itself, so you change the contract to:
url="https://github.com/near/near-sdk-js/blob/develop/examples/src/basic-updates/basic-updates-update.js"
start="21" end="43" />
+
+
+
#### Incompatible States
@@ -186,12 +200,16 @@ state, removes the `payments` vector and adds the information to the
url="https://github.com/near/near-sdk-js/blob/develop/examples/src/basic-updates/basic-updates-update.js"
start="5" end="68" />
+
+
+
-Notice that `migrate` is actually an
-[initialization method](../anatomy/storage)
-that **ignores** the existing state (`[#init(ignore_state)]`), thus being able
-to execute and rewrite the state.
+Notice that in Rust/JS, `migrate` is actually an [initialization method](../anatomy/storage) that **ignores** the existing state (`[#init(ignore_state)]`), thus being able to execute and rewrite the state.
+
+In Go, `Migrate()` is a regular `@contract:mutating` method that reads the old state manually using `env.StateRead()`, transforms it, and writes the new state back — no special init directive is needed.
diff --git a/smart-contracts/testing/integration-test.mdx b/smart-contracts/testing/integration-test.mdx
index 6d4eb7c0dee..73577ea8eb4 100644
--- a/smart-contracts/testing/integration-test.mdx
+++ b/smart-contracts/testing/integration-test.mdx
@@ -15,7 +15,13 @@ Moreover, when using the local `sandbox` you gain complete control of the networ
In NEAR, integration tests are implemented using a framework called **Workspaces**. Workspaces comes in two flavors: [🦀 Rust](https://github.com/near/workspaces-rs) and [🌐 Typescript](https://github.com/near/workspaces-js).
-All of our [examples](https://github.com/near-examples) come with integration testing.
+For **Go contracts**, unit tests are run with the `near-go` CLI:
+
+```bash
+near-go test project
+```
+
+This runs unit tests using the `MockSystem` environment to simulate the NEAR blockchain locally — no sandbox required. For full integration tests that deploy to a sandbox, Go contracts use the Rust `workspaces-rs` framework. See the [Go Integration Testing](#go-integration-testing) section below.
**Sandbox Testing**
@@ -559,24 +565,169 @@ See a [TypeScript example of spooning](https://github.com/near/workspaces-js/blo
Lets take a look at the test of our [Quickstart Project](../quickstart) [👋 Hello NEAR](https://github.com/near-examples/hello-near-examples), where we deploy the contract on an account and test it correctly retrieves and sets the greeting.
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
### Snippet II: Testing Donations
In most cases we will want to test complex methods involving multiple users and money transfers. A perfect example for this is our [Donation Example](https://github.com/near-examples/donation-examples), which enables users to `donate` money to a beneficiary. Lets see its integration tests
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+---
+
+## Go Integration Testing
+
+Go contracts use the Rust `workspaces-rs` framework for integration tests. The workflow is:
+1. Build your contract to WASM with `near-go build`
+2. Write integration tests in a separate Rust project using `workspaces-rs`
+3. Run with `cargo test`
+
+
+**Unit tests vs Integration tests**
+
+`near-go test project` runs **unit tests** using the `MockSystem` environment locally. For full integration tests that deploy to a sandbox, use the `workspaces-rs` Rust crate as described below.
+
+
+### Project Structure
+
+```bash
+my-contract/
+├── main.go # your Go contract
+├── go.mod
+├── go.sum
+└── integration-tests/ # Standalone Rust integration test project
+ ├── Cargo.toml
+ └── tests/
+ └── test_my_contract.rs
+```
+
+### Running Tests
+
+```bash
+# Step 1: Build the Go contract to WASM (from the contract root)
+near-go build
+
+# Step 2: Run integration tests
+cargo test --manifest-path integration-tests/Cargo.toml
+```
+
+### Writing Integration Tests
+
+Integration tests follow the `workspaces-rs` pattern. The compiled `main.wasm` sits one directory above `integration-tests/`, so tests load it with `std::fs::read("../main.wasm")`.
+
+
+**Double-encoded return values**
+
+`near-sdk-go` wraps every return value in an extra JSON string encoding. When reading a view result, always deserialize in **two steps**: first parse the outer `String`, then parse the inner type.
+
+```rust
+// ✅ Correct two-step deserialization
+let raw: String = contract.view("get_greeting").args_json(json!({})).await?.json()?;
+let greeting: String = serde_json::from_str(&raw)?;
+
+// ❌ Will fail – skips the outer wrapper
+let greeting: String = contract.view("get_greeting").args_json(json!({})).await?.json()?;
+```
+
+
+```rust
+// integration-tests/tests/test_greeting.rs
+use serde_json::json;
+
+#[tokio::test]
+async fn test_greeting_init() -> anyhow::Result<()> {
+ let sandbox = near_workspaces::sandbox().await?;
+ let wasm = std::fs::read("../main.wasm")?;
+ let contract = sandbox.dev_deploy(&wasm).await?;
+
+ contract
+ .call("init")
+ .args_json(json!({}))
+ .transact()
+ .await?
+ .into_result()?;
+
+ // Two-step deserialization: outer String wrapper → inner type
+ let raw: String = contract.view("get_greeting").args_json(json!({})).await?.json()?;
+ let greeting: String = serde_json::from_str(&raw)?;
+ assert_eq!(greeting, "Hello");
+ Ok(())
+}
+
+#[tokio::test]
+async fn test_set_greeting() -> anyhow::Result<()> {
+ let sandbox = near_workspaces::sandbox().await?;
+ let wasm = std::fs::read("../main.wasm")?;
+ let contract = sandbox.dev_deploy(&wasm).await?;
+
+ contract.call("init").args_json(json!({})).transact().await?.into_result()?;
+
+ let caller = sandbox.dev_create_account().await?;
+ caller
+ .call(contract.id(), "set_greeting")
+ .args_json(json!({ "greeting": "Howdy" }))
+ .transact()
+ .await?
+ .into_result()?;
+
+ let raw: String = contract.view("get_greeting").args_json(json!({})).await?.json()?;
+ let greeting: String = serde_json::from_str(&raw)?;
+ assert_eq!(greeting, "Howdy");
+ Ok(())
+}
+```
+
+```toml
+# integration-tests/Cargo.toml
+[package]
+name = "greeting-integration-tests"
+version = "0.1.0"
+edition = "2021"
+
+[dev-dependencies]
+near-workspaces = "0.22.0"
+tokio = { version = "1.41.1", features = ["full"] }
+anyhow = "1.0.93"
+serde_json = "1.0.133"
+serde = { version = "1.0.215", features = ["derive"] }
+```
+
+
+
+You can also test your Go contract on NEAR testnet by deploying it first with `near deploy` and calling methods via the `near` CLI.
+
+[See full example on GitHub](https://github.com/Emir-Asanov/near-go-examples/blob/example-release-1/greeting/integration-tests/tests/test_greeting.rs)
+
+
---
diff --git a/smart-contracts/testing/unit-test.mdx b/smart-contracts/testing/unit-test.mdx
index 667adb83046..f9ae32a2c7e 100644
--- a/smart-contracts/testing/unit-test.mdx
+++ b/smart-contracts/testing/unit-test.mdx
@@ -13,20 +13,45 @@ If you used one of our [examples](https://github.com/near-examples/docs-examples
You can run `yarn test` from the root folder of each project to run both unit and [integration](/smart-contracts/testing/integration-test) tests.
+For **Go contracts**, unit tests must be run with the `near-go` CLI, which uses **TinyGo** under the hood. The SDK's `system` package uses TinyGo-specific `//go:wasmimport` declarations that cannot be compiled by the standard Go toolchain.
+
+```bash
+# Run tests for the entire project
+near-go test project
+
+# Run tests only for the current package
+near-go test package
+```
+
+Go unit tests use the `MockSystem` from `near-sdk-go` to simulate the NEAR environment locally without a blockchain.
+
+
+**Prerequisites**
+
+You need the `near-go` CLI installed. If you followed the [Quickstart](../quickstart), everything is already set up — TinyGo and all dependencies are handled by the `install.sh` script automatically.
+
+
---
## Snippet I: Testing a Counter
The tests in the [Counter Example](https://github.com/near-examples/counters) rely on basic functions to check that the `increment`, `decrement`, and `reset` methods work properly.
-
-
+
+
-
+
+
+
+
+
---
@@ -34,14 +59,21 @@ The tests in the [Counter Example](https://github.com/near-examples/counters) re
While doing unit testing you can modify the [Environment variables](../anatomy/environment) through the `VMContextBuilder`. This will enable you to, for example, simulate calls from different users, with specific attached deposit and GAS. Here we present a snippet on how we test the `donate` method from our [Donation Example](https://github.com/near-examples/donation-examples) by manipulating the `predecessor` and `attached_deposit`.
-
-
+
+
-
+
+
+
+
+
---
diff --git a/smart-contracts/what-is.mdx b/smart-contracts/what-is.mdx
index 02a3c073db4..8889bbece4c 100644
--- a/smart-contracts/what-is.mdx
+++ b/smart-contracts/what-is.mdx
@@ -76,7 +76,7 @@ The development flow can be summarized as follows:
- [**Monitor**](../data-infrastructure/what-is): The contract's activity can be monitored through simple APIs.
#### Supported Languages
-During the whole cycle, developers can choose between [JavaScript](https://www.learn-js.org/) and [Rust](https://www.rust-lang.org/), allowing them to use their favorite language at each step of their journey.
+During the whole cycle, developers can choose between [JavaScript](https://www.learn-js.org/), [Rust](https://www.rust-lang.org/), and [Go](https://go.dev/), allowing them to use their favorite language at each step of their journey.