Ethereum to NEAR

As an Ethereum developer, what should I know about NEAR's models and practices?

NEAR 102 Course

NEAR 102: Introduction to NEAR for Ethereum Developers

ETH v.s. NEAR

Side-by-side comparion between ETH v.s. NEAR for developers

  1. Infrastructure

    • scalability and speed
    • cost
  2. Account Model

    • account v.s. address
    • sub-accounts
    • single key pair v.s. multiple key pairs
    • key models: full access v.s. function call key
  3. Solidity v.s Rust (AssemblyScript)

    • cross-contract call
    • (de)serialization
  4. Deployment and Versioning

    • upgradability

Rust Smart Contract

WebAssembly

Docs

Best Practices

Reference

  1. Austin Abell, Comparing Modern Smart Contracting Environments: Writing a smart contract with Solidity, NEAR, ink!, Solana, and CosmWasm, 2021/03/22

Play with NEAR CLI

Create xxx.near or xxx.testnet Account

near call testnet create_account \
    '{
        "new_account_id": "xxxxxxx.testnet", 
        "new_public_key": "ed25519:DAh6wwBYbakodZaA3QSZvojBF7EceeUkZsdELTEBRoYt"
    }' \
    --accountId bot.testnet --deposit 0.1

Read Parameters from JSON File

The JSON file could be params.json, with content

{
    "new_account_id": "xxxxxxx.testnet",
    "new_public_key": "ed25519:DAh6wwBYbakodZaA3QSZvojBF7EceeUkZsdELTEBRoYt"
}

Run the command below to create the account:

near call testnet create_account "$(< params.json)" --accountId bot.testnet --deposit 0.1

Send NEP141 token to another account

Here we take Banana from BerryClub for example.

Suppose we want to transfer 1 Banana from the user robertyan.testnet to linus.testnet.

  1. If the receiver hasn't regisered on NEP141 before, we first need to deposit storage cost for the receiver linus.testnet (i.e. register the account on the NEP141 contract) with a small amount of NEAR.
  2. Then we'll be able to call ft_transfer to transfer Banana to the receiver. Since the precision of Banana is 18 digits, the amount is 1000000000000000000 for sending 1 Banana

TestNet

You need to run storage_deposit first to register the receiver account on Banana contract berryclub.testnet

near call berryclub.testnet \
    storage_deposit '{"account_id": "linus.testnet"}' \
    --account-id robertyan.testnet --amount 0.025

Then you can send some tokens to the receiver

near call berryclub.testnet ft_transfer \
    '{
        "receiver_id": "linus.testnet", 
        "amount": "1000000000000000000"
    }' \
    --account-id robertyan.testnet --amount 0.000000000000000000000001

MainNet

You need to run storage_deposit first to register the receiver account on Banana contract berryclub.ek.near

near call berryclub.ek.near storage_deposit '{"account_id": "linus.near"}' \
    --account-id robertyan.near --amount 0.025

Then you can send some tokens to the receiver

near call berryclub.ek.near ft_transfer \
    '{
        "receiver_id": "linus.near", 
        "amount": "1000000000000000000"
    }' \
    --account-id robertyan.near --amount 0.000000000000000000000001

Flexible Account Model

  1. NEAR Account Introduction: https://docs.near.org/docs/concepts/account | https://medium.com/nearprotocol/an-introduction-to-near-accounts-f96cb84ef091
  2. NEAR Account Spec: https://nomicon.io/DataStructures/Account.html
  3. NEAR Wallet: MainNet https://wallet.near.org/ | TestNet https://wallet.testnet.near.org/ (GitHub: https://github.com/near/near-wallet)
  4. NEAR Explorer: MainNet https://explorer.near.org/ | TestNet https://explorer.testnet.near.org/ (GitHub: https://github.com/near/near-explorer)

Hiding the Blockchain

Storage

Storage Staking on NEAR

Storage Management Standard

Decentralized Storage

Filecoin

  • Filecoin-NEAR Bridge: https://near.storage
  • Grant: https://github.com/filecoin-project/devgrants/blob/master/rfps/near-and-filecoin.md

Crust

  • Post: https://medium.com/nearprotocol/crust-network-partners-with-near-protocol-to-introduce-decentralized-storage-solutions-a17edeaf29bb

Arweave

Ceramic

Sia

Wallet

Topics

  1. Wallet Login
  2. Account Creation
  3. Tokens: Fungible Tokens / Non-Fungible Tokens
  4. Linkdrop

Fungible Token

Awesome FTs Projects and Toolkits built with NEAR

Standards

Fungible Token Standards

Toolkits

Core Contracts with FT Support

  • linkdrop
  • lockup

DApps

Token List

  • https://tkn.farm/

Frequently Used Tokens

NameMainnet AddressTestnet Address
USDTdac17f958d2ee523a2206206994597c13d831ec7.factory.bridge.nearusdt.fakes.testnet
DAI6b175474e89094c44da98b954eedeac495271d0f.factory.bridge.neardai.fakes.testnet
USDCa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.nearusdc.fakes.testnet
wNEARwrap.nearwrap.testnet
REFtoken.v2.ref-finance.nearref.fakes.testnet
SKYWARDtoken.skyward.near
OCTf5cfbc74057c610c8ef151a439252680ac68c6dc.factory.bridge.nearoct.beta_oct_relay.testnet
PARAStoken.paras.nearparas.fakes.testnet
stNEARmeta-pool.near
PULSE52a047ee205701895ee06a375492490ec9c597ce.factory.bridge.nearpulse.fakes.testnet
BANANAberryclub.ek.nearbanana.ft-fin.testnet
WETHc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.factory.bridge.nearweth.fakes.testnet
1INCH111111111117dc0aa78b770fa6a738034120c302.factory.bridge.near
GRTc944e90c64b2c07662a292be6244bdf05cda44a7.factory.bridge.near
CUCUMBERfarm.berryclub.ek.near
HT6f259637dcd74c767781e37bc6133cd6a68aa161.factory.bridge.near
GTCde30da39c46104798bb5aa3fe8b9e0e1f348163f.factory.bridge.near
UNI1f9840a85d5af5bf1d1762f925bdaddc4201f984.factory.bridge.near
WBTC2260fac5e5542a773aa44fbcfedf7c193bc2c599.factory.bridge.near
LINK514910771af9ca656af840dff83e8264ecf986ca.factory.bridge.near
HAPId9c2d319cd7e6177336b0a9c93c21cb48d84fb54.factory.bridge.nearhapi.fakes.testnet
1MILa4ef4b0b23c1fc81d3f9ecf93510e64f58a4a016.factory.bridge.near
marmajmarmaj.tkn.near
Cheddartoken.cheddar.near
ETHauroraeth.fakes.testnet
PXTpixeltoken.near
DBIOdbio.near
AURORAaaaaaa20d9e0e2461697782ef11675f668207961.factory.bridge.nearaurora.fakes.testnet
$METAmeta-token.near
POTATOv1.dacha-finance.near
FLX3ea8ea4237344c9931214796d9417af1a1180770.factory.bridge.near
UMINTe99de844ef3ef72806cf006224ef3b813e82662f.factory.bridge.near
nUSDOv3.oin_finance.near
OIN9aeb50f542050172359a0e1a25a9933bc8c01259.factory.bridge.near
MYRIAmyriadcore.near

Non-Fungible Token

Awesome NFTs Projects and Toolkits built with NEAR

Standards

Non-Fungible Token Standards

Example Projects

NFT Contract Examples

  1. NFT Example with near-contract-standard: Example implementation of a non-fungible token contract which uses near-contract-standards and simulation tests.
  2. NFT Contract Simple Example: simple example of how NFT contract looks like
  3. NFT Contract Standard Implementation: NFT implementation in NEAR Rust SDK

Mint NFTs Tutorials

  1. Minting NFTs: In this tutorial you'll learn how to easily create your own NFTs without doing any software development by using a readily-available smart contract and a decentralized storage solution like IPFS.
  2. Create NFTs in Minecraft: This tutorial will run you through minting Minecraft structures of any size onto the NEAR blockchain.

NFT Market Examples

  1. NFT Marketplace (demo): PoC for NFT market projects
  2. NFT Launcher: launch NFT project with easy user on-boarding

REST API

  1. NEAR REST API Server: perform blockchain calls with the simple REST API, with NFT support such as mint NFT, transfer NFT, view NFT owners with the POST/GET requests

More NFT Examples

  1. 10K: Template for making a NFT contract with a raffle of tokens

DApps

  1. Mintbase: Digital assets minted and backed by you
  2. Paras: Create, Trade and Collect Digital Art Cards (DACs)
  3. Too Rich 秃力富房产: NFT digital art exhibited on Taobao Maker Festival
  4. NFT Hiphop: NFT collection that featured artists from the 70s to the present, built by Universal Hip Hop Museum, The NEAR Protocol, and Ed Young
  5. more NFT projects on NEAR

Game

Why Building Games on NEAR?

1. Fast and Scalable

NEAR’s sharded infrastructure make it possible to scale infinitely. The transactions like transfer can be completed instantly on NEAR, and with very low gas fees.

The real-time transaction, low gas fee and high scalability makes it possible to launch world-class game with millions of users.

2. Great User Experience

The NEAR Account and Keys model, Wallet and APIs make it easy to build games with great user experience on NEAR.

The NEAR account is human readable, which makes it able to be used directly for roles in games.

The Function Call Keys make it possible to create secure keys with restricted access.

With the help of NEAR's function call key, web wallet and near APIs, it's quite easy to integrate NEAR with game engines or your web/mobile frontend, and user don't have to install browser extensions to launch the game in web or mobile app.

The progressive onboarding process makes it quite easy for users to create a new account using email or SMS, without the necessity of learning about the concepts of seed phrases, gas fees, etc.

3. Developer Friendly

With NEAR, developers can build smart contracts with common languages that they're already familiar with, such as Rust, TypeScript, or build with Solidity on Aurora if they prefer EVM toolings.

Developers can also build the games with NEAR libraries in JavaScript, Python, or APIs integrated with game frameworks such as Unity, Godot.

Also, 30% of the gas fees spent on the contracts will be paid to developers.

4. Cross-Chain Interoperability

Assets including FTs / NFTs can be bridged to Ethereum efficiently and easily with the help of Rainbow Bridge.

Read more about why NEAR is attractive to developers on https://near.org/developers/

Build Games on NEAR

Read the best practices shared by Human Guild:

  1. How to Use NEAR in a Game
  2. Game Economics

Learn with Examples

To learn with concrete examples about how to build games on NEAR, we recommend that you start with the examples of BerryClub and NEAR Lands:

  1. BerryClub: source code
  2. NEAR Lands: source code

Game Engines / Frameworks

Unity3D

Godot

Cocos2D

Games built on NEAR

Examples

Community

  • Human Guild
    • connected thousands of people
    • Creator Economy / Gaming
    • help dozens of projects launch on NEAR

Workshop

NEAR RPCs

Mainnet

ProviderEndpoint Root
NEARhttps://rpc.mainnet.near.org
Pagodahttps://near-mainnet.api.pagoda.co/rpc/v1
1RPChttps://1rpc.io/near
All That Nodehttps://near-mainnet-rpc.allthatnode.com:3030
ankr.comhttps://rpc.ankr.com/near
fast-nearhttps://rpc.web4.near.page
Gateway.fmhttps://rpc.near.gateway.fm/
GetBlockhttps://getblock.io/nodes/near/
Lavender.Five Nodeshttps://near.lavenderfive.com/
NodeRealhttps://nodereal.io/api-marketplace/near-rpc
NOWNodeshttps://near.nownodes.io/
OMNIAhttps://endpoints.omniatech.io/v1/near/mainnet/public
QuickNode-
Seraclehttps://api.seracle.com/saas/baas/rpc/near/mainnet/public/

Archival Nodes

ProviderEndpoint Root
NEARhttps://archival-rpc.mainnet.near.org
ankr.comhttps://rpc.ankr.com/near
fast-nearhttps://rpc.web4.near.page

Testnet

ProviderEndpoint Root
NEARhttps://rpc.testnet.near.org
Pagodahttps://near-testnet.api.pagoda.co/rpc/v1
All That Nodehttps://near-testnet-rpc.allthatnode.com:3030
fast-nearhttps://rpc.web4.testnet.page/
OMNIAhttps://endpoints.omniatech.io/v1/near/testnet/public

Archival Nodes

ProviderEndpoint Root
NEARhttps://archival-rpc.testnet.near.org
fast-nearhttps://rpc.web4.testnet.page

Indexer

Introduction

Usage

NEAR Indexer for Explorer

Shared Public Access

NEAR runs the indexer and maintains it for NEAR Explorer, NEAR Wallet, and some other internal services. It proved to be a great source of data for various analysis and services, so we decided to give a shared read-only public access to the data:

  • testnet credentials: postgres://public_readonly:nearprotocol@35.184.214.98/testnet_explorer
  • mainnet credentials: postgres://public_readonly:nearprotocol@104.199.89.51/mainnet_explorer

Database Structure

Common SQL Recipes

Total New Accounts By Date

SELECT
        TIMESTAMP ‘epoch’ + DIV(DIV(blocks.block_timestamp, 1000000000), 60 * 60 * 24) * INTERVAL ‘1 day’ AS “date”,
        COUNT(*) as new_accounts_count_by_date
FROM accounts
JOIN receipts ON receipts.receipt_id = accounts.created_by_receipt_id
JOIN blocks ON blocks.block_hash = receipts.included_in_block_hash
GROUP BY “date”
ORDER BY “date”

Top 10 Accounts with most Transactions in Past Two Weeks

SELECT signer_account_id,
        COUNT(*) AS transactions_count
FROM transactions
WHERE transactions.block_timestamp >= (cast(EXTRACT(EPOCH FROM NOW()) - 60 * 60 * 24 * 14 AS bigint) * 1000 * 1000 * 1000)
AND transactions.block_timestamp < (cast(EXTRACT(EPOCH FROM NOW()) AS bigint) * 1000 * 1000 * 1000)
GROUP BY signer_account_id
ORDER BY transactions_count DESC
LIMIT 10

Active Accounts by Date

SELECT
        TIMESTAMP 'epoch' + DIV(DIV(transactions.block_timestamp, 1000000000), 60 * 60 * 24) * INTERVAL '1 day' AS "date",
        COUNT(distinct transactions.signer_account_id) as active_accounts_count_by_date
      FROM transactions
      JOIN execution_outcomes ON execution_outcomes.receipt_id = transactions.converted_into_receipt_id
      WHERE execution_outcomes.status IN ('SUCCESS_VALUE', 'SUCCESS_RECEIPT_ID') and 
      TIMESTAMP 'epoch' + DIV(DIV(transactions.block_timestamp, 1000000000), 60 * 60 * 24) * INTERVAL '1 day' > current_date -7 
      GROUP BY "date"
      ORDER BY "date"

Real-World Examples

BerryClub Wayback

BerryClub Wayback used indexer to fetch 50 edits randomly from all the edits or by a user

find edits: source code

Fetch 50 edits randomly from all the edits or edits by a user

async function findEdits(accountId) {
    console.log('findEdits', accountId);

    return withPgClient(async client => {
        const { rows } = await client.query(`
            SELECT included_in_block_timestamp AS block_timestamp, included_in_block_hash AS block_hash
            FROM receipts ${accountId ? '' : `TABLESAMPLE SYSTEM(0.05)`}
            WHERE receiver_account_id = 'berryclub.ek.near'
                ${accountId ? `AND predecessor_account_id = $1` : ''}
            ORDER BY random()
            LIMIT 50
            `, accountId ? [accountId] : []);

        console.log(`Found ${rows.length} rows`);
        return rows;
    })
}

Wallet Contract Helper

NEAR Wallet used indexer to query necessary information related to accounts, such as activities, FTs/NFTs owned by the user, etc.

findAccountActivity: source code

Show account activities in wallet

const findAccountActivity = async (ctx) => {
    const { accountId } = ctx.params;
    let { offset, limit = 10 } = ctx.request.query;
    if (!offset) {
        offset = '9999999999999999999';
    }
    const { rows } = await pool.query(`
        select
            included_in_block_hash block_hash,
            included_in_block_timestamp block_timestamp,
            originated_from_transaction_hash hash,
            index_in_action_receipt action_index,
            predecessor_account_id signer_id,
            receiver_account_id receiver_id,
            action_kind,
            args
        from action_receipt_actions
        join receipts using(receipt_id)
        where
            receipt_predecessor_account_id != 'system' and
            (receipt_predecessor_account_id = $1 or receipt_receiver_account_id = $1) and
            $2 > receipt_included_in_block_timestamp
        order by receipt_included_in_block_timestamp desc
        limit $3
        ;
    `, [accountId, offset, limit]);

    ctx.body = rows;
};

findAccountsByPublicKey: source code

Find accounts by public key, so when you input seed phrase, wallet is able to know which account is related to this seed phrase.

const findAccountsByPublicKey = async (ctx) => {
    const { publicKey } = ctx.params;
    const { rows } = await pool.query(`
        SELECT DISTINCT account_id
        FROM access_keys
        JOIN accounts USING (account_id)
        WHERE public_key = $1
            AND accounts.deleted_by_receipt_id IS NULL
            AND access_keys.deleted_by_receipt_id IS NULL
    `, [publicKey]);
    ctx.body = rows.map(({ account_id }) => account_id);
};

findLikelyTokens: source code

List the FT tokens related to the current user.

const findLikelyTokens = async (ctx) => {
    const { accountId } = ctx.params;

    const received = `
        select distinct receipt_receiver_account_id as receiver_account_id
        from action_receipt_actions
        where args->'args_json'->>'receiver_id' = $1
            and action_kind = 'FUNCTION_CALL'
            and args->>'args_json' is not null
            and args->>'method_name' in ('ft_transfer', 'ft_transfer_call')
    `;

    const mintedWithBridge = `
        select distinct receipt_receiver_account_id as receiver_account_id from (
            select args->'args_json'->>'account_id' as account_id, receipt_receiver_account_id
            from action_receipt_actions
            where action_kind = 'FUNCTION_CALL' and
                receipt_predecessor_account_id = $2 and
                args->>'method_name' = 'mint'
        ) minted_with_bridge
        where account_id = $1
    `;

    const calledByUser = `
        select distinct receipt_receiver_account_id as receiver_account_id
        from action_receipt_actions
        where receipt_predecessor_account_id = $1
            and action_kind = 'FUNCTION_CALL'
            and (args->>'method_name' like 'ft_%' or args->>'method_name' = 'storage_deposit')
    `;

    const { rows } = await pool.query([received, mintedWithBridge, calledByUser].join(' union '), [accountId, BRIDGE_TOKEN_FACTORY_ACCOUNT_ID]);
    ctx.body = rows.map(({ receiver_account_id }) => receiver_account_id);
};

findLikelyNFTs: source code

List the NFT tokens related to the current user.

const findLikelyNFTs = async (ctx) => {
    const { accountId } = ctx.params;

    const received = `
        select distinct receipt_receiver_account_id as receiver_account_id
        from action_receipt_actions
        where args->'args_json'->>'receiver_id' = $1
            and action_kind = 'FUNCTION_CALL'
            and args->>'args_json' is not null
            and args->>'method_name' like 'nft_%'
    `;

    // TODO: How to query minted tokens?

    const { rows } = await pool.query([received].join(' union '), [accountId]);
    ctx.body = rows.map(({ receiver_account_id }) => receiver_account_id);
};

Aurora

Introduction to Aurora

Design & Discussion

Dev Docs

Tooling

Rainbow Bridge

Frontend

Design

Repositories

Community

Contract

Localization

Build DApp with Localization in Mind

In the Open Web world, we should keep in mind your apps will be accessed not only by users who speak English, but also people all around the world.

Considering around 30~40% of the users may actaully come from districts like China, Korea, Vietnam, etc., it's important to build your decentralized application with localization ready from the beginning.

To be specific, DApp localization means:

  1. i18n: Your DApp's user interface has i18n support from the beginning. So it costs minimal extra efforts to refator your code and add support to other languages.
  2. Web app deployment: Since the frontend of most DApps nowadays are actually web applications. The deployment of your frontend code, espeically the static sites with JAMStack (Next.js, Nuxt.js, etc.), should choose the appropriate CI/CD, and CDN solutions to make your site be able to be open in less than 3 seconds rather than 30 seconds which will scare away your users.
  3. RPC Node and Indexer: For NEAR RPC Node, and indexers, choose the nodes or servers that are most appropriate for your infrastructure and users to reduce the latency and improve stability.
  4. Decentralized Storage: For decentralized storage solutions such IPFS, Arweave, Sia, Ceramic, etc. choose an appropriate service provider which help make your images, videos, etc. accessible globally by default without much extra efforts.
  5. Backend Services: For any other dependent serivces, such as any helper services built with indexers, or other relevant data services, try to make the services accessible globally.

Internalization (i18n)

We suggest the DApp developers to build your web application with i18n from the first day.

React

You can use react-i18next or any other frameworks you prefer if you're building with React or React Native.

Or you may use react-localize-redux in case you don't want to keep the language route such as en, zh in your URLs.

Vue

vue-i18n may be a good solution for Vue developers, or you can simply use nuxt/i18n module if you're building with Nuxt.js.

Need Help with Translation?

In case you need help about translation your DApp into any languages, create an issue here following the template, and we'll help connect you with the community to set up bounties and ask verified translators to help with the translation of your project.

Web App Deployment

Static Site Deployment

If you're deploying your web app as a static site or SPA (Single Page Application), and would like to make your site easily accessible with fast loading speed in all areas including China, you need to carefully choose the deployment services, otherwise it's possible your users will complain why your sites take 30~60 seconds to open.

Unfortunately, the most popular static site hosting solutions simply doesn't work globally especially in China, including Netlify, Vercel, Render, etc.

The current recommended solution is Cloudflare Pages which has verified to be working well even if your JS bundle may be as large as 1M.

RPC Nodes and Indexers

RPC Node

If your app requires to use custom RPC Nodes, NEAR Protocol is working with RPC Node service providers such as Figment DataHub, Bison Trails, BSN (Blockchain-based Service Network), NEAR DRPC, etc., which will help to set up the best solutions for your DApps.

Please feel free to contact email: robert AT near DOT org if you need nodes from China, we can help to set up optimized RPC nodes for your DApps.

Indexer

For most DApps, now you can use the public indexer server as specifid in the indexer chapter, but in case you need any customized Indexer server, you can try to set up your own or discuss with us in community.

Decentralized Storage

NEAR Procotol is working closely with Decentralized Storage solutions such as Filecoin, Ceramic, Crust, etc., to make it extremely easy to integrate your DApp with decentralized storage solutions.

IPFS

You should look at the Filecoin-NEAR Bridge bulit by Textile if you're building with IPFS which provide free storage for NEAR users: https://near.storage/

Also we'd like to recommend you to look at the IPFS service providers such as ......, in case you'd like to have built-in CDN ready for your IPFS files.

Ceramic

To Be Added

Arweave

To Be Added

Backend Services

For backend services who'd like to achieve globally accessibility, you need to route your users to the optimal servers to reduce the latency.

Examples

  1. Mintbase
  2. Paras
  3. Pulse
  4. Ref Finance
  5. Aurora
  6. Rainbow Bridge
  7. SputnikDAO

Security

  • NEAR Standard Contracts: https://github.com/near/near-sdk-rs/tree/master/near-contract-standards