Documentation Index
Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
Use this file to discover all available pages before exploring further.
Overview
NEAR uses the Ed25519 elliptic curve — the same as Solana. NEAR implicit accounts are simply the hex-encoded 32-byte public key. Transactions use Borsh serialization and are submitted via JSON-RPC.
| Property | Value |
|---|
| Curve | Ed25519 |
| Root Wallet | Solana |
| Address Format | 64-char hex string (implicit account) |
| Hashing | SHA-256 |
| Serialization | Borsh |
| Smallest Unit | yoctoNEAR (1 NEAR = 10^24 yoctoNEAR) |
Dependencies
npm install @noble/curves @noble/hashes bs58
Derive Address
NEAR implicit accounts are the hex-encoded Ed25519 public key — the simplest derivation of all chains:
import bs58 from "bs58";
function deriveNearAddress(solanaAddress: string): string {
return bytesToHex(bs58.decode(solanaAddress)); // 64-char hex string
}
Sign a Message
Sign the raw UTF-8 bytes of the message using the Solana wallet:
async function signNearMessage(message: string): Promise<string> {
const wallet = dynamicClient.wallets.userWallets.find(w => w.chain === "SOL")!;
const messageBytes = new TextEncoder().encode(message);
const { signedMessage } = await dynamicClient.wallets.signMessage({
wallet,
message: bytesToHex(messageBytes),
});
return signedMessage;
}
Sign a Transaction
Build a Borsh-serialized transaction, sign with the Solana wallet, and submit via JSON-RPC:
import { sha256 } from "@noble/hashes/sha2";
import bs58 from "bs58";
async function sendNearTransfer(
recipient: string,
amountNear: number,
nearAddress: string,
): Promise<string> {
const wallet = dynamicClient.wallets.userWallets.find(w => w.chain === "SOL")!;
const pubkey = bs58.decode(wallet.address);
const [wholePart, fracPart = ""] = amountNear.toString().split(".");
const paddedFrac = fracPart.padEnd(24, "0").slice(0, 24);
const amountYocto = BigInt(wholePart + paddedFrac);
const [accessKeyResult, blockResult] = await Promise.all([
nearRpc("query", {
request_type: "view_access_key",
finality: "final",
account_id: nearAddress,
public_key: `ed25519:${bs58.encode(pubkey)}`,
}),
nearRpc("block", { finality: "final" }),
]);
const nonce = BigInt(accessKeyResult.nonce) + BigInt(1);
const blockHash = bs58.decode(blockResult.header.hash);
const txBytes = concatBytes(
borshString(nearAddress),
borshU8(0), // PublicKey variant: ed25519
pubkey, // 32-byte pubkey
borshU64(nonce),
borshString(recipient),
blockHash, // 32-byte block hash
borshU32(1), // number of actions
borshU8(3), // Transfer action variant
borshU128(amountYocto),
);
const txHash = sha256(txBytes);
const { signedMessage } = await dynamicClient.wallets.signMessage({
wallet,
message: bytesToHex(txHash),
});
const sigBytes = decodeSig(signedMessage);
const signedTxBytes = concatBytes(txBytes, borshU8(0), sigBytes);
const signedTxBase64 = btoa(String.fromCharCode(...signedTxBytes));
const result = await nearRpc("broadcast_tx_commit", [signedTxBase64]);
return result.transaction.hash;
}
For the Borsh serialization primitives (borshU8, borshU32, borshU64, borshU128, borshString) and nearRpc helper, see the JavaScript NEAR guide. The only difference in React Native is the signing call.
Verify a Signature
import { ed25519 } from "@noble/curves/ed25519";
import bs58 from "bs58";
function verifyNearSignature(
message: string,
signature: string,
solanaAddress: string,
): boolean {
const pubkey = bs58.decode(solanaAddress);
const messageBytes = new TextEncoder().encode(message);
const sigBytes = decodeSig(signature);
return ed25519.verify(sigBytes, messageBytes, pubkey);
}
Check Balance
async function getNearBalance(accountId: string): Promise<string> {
const res = await fetch("https://rpc.testnet.near.org", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
id: "1",
method: "query",
params: { request_type: "view_account", finality: "final", account_id: accountId },
}),
});
const data = await res.json();
if (data.error) return "0";
const yocto = BigInt(data.result.amount);
const divisor = BigInt(10) ** BigInt(24);
const whole = yocto / divisor;
const frac = (yocto % divisor).toString().padStart(24, "0").replace(/0+$/, "");
return frac.length === 0 ? whole.toString() : `${whole}.${frac}`;
}