From 3619f6bc26df664995738ca9b98f88c15c9e4ec0 Mon Sep 17 00:00:00 2001 From: Matias Benary Date: Tue, 14 Oct 2025 22:46:56 -0300 Subject: [PATCH 1/5] feat:add near connect tutorial --- docs/web3-apps/concepts/web-login.md | 2 +- .../tutorials/web-login/near-conector.md | 511 ++++++++++++++++++ website/sidebars.js | 1 + 3 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 docs/web3-apps/tutorials/web-login/near-conector.md diff --git a/docs/web3-apps/concepts/web-login.md b/docs/web3-apps/concepts/web-login.md index ef4c4c2b1dd..2f48b0e3c76 100644 --- a/docs/web3-apps/concepts/web-login.md +++ b/docs/web3-apps/concepts/web-login.md @@ -50,7 +50,7 @@ Considered a successor to the wallet selector, the [NEAR Connector](https://gith :::tip -Check our [NEAR Connector Integration Example](https://github.com/near-examples/hello-near-connector) to learn how to integrate the NEAR Connector into your web app +You can learn how to integrate the wallet selector into your app in our [NEAR Connector](../tutorials/web-login/near-conector.md) guide. ::: diff --git a/docs/web3-apps/tutorials/web-login/near-conector.md b/docs/web3-apps/tutorials/web-login/near-conector.md new file mode 100644 index 00000000000..dee7d6ecea8 --- /dev/null +++ b/docs/web3-apps/tutorials/web-login/near-conector.md @@ -0,0 +1,511 @@ +--- +id: near-conector +title: NEAR Connect Tutorial +description: "Connect users to NEAR wallets with a secure, sandbox-based connector library" +--- +import {CodeTabs, Language, Github} from "@site/src/components/UI/Codetabs" +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +The `@hot-labs/near-connect` library provides a secure, zero-dependency wallet connector for NEAR blockchain with a unique sandbox-based architecture. Unlike traditional wallet selectors, it offers a dynamic manifest system that allows wallets to be added and updated without requiring developers to update their dependencies. + +![Preview](https://github.com/user-attachments/assets/c4422057-38bb-4cd9-8bd0-568e29f46280) + +:::tip Why NEAR Connect? + +- **Secure Execution**: Wallet scripts run in isolated sandboxed iframes for maximum security +- **Dynamic Wallets**: Wallets are loaded from a manifest and can be updated without code changes +- **Zero Dependencies**: Lightweight library with no external dependencies +- **Automatic Detection**: Supports both injected wallets (extensions) and manifest-based wallets + +::: + +:::info + +Check other options to let users login into your application and use NEAR accounts in the [Web Login](../../concepts/web-login.md) section + +::: + +:::tip Working Example + +For a complete working example with React, check out the [hello-near-connector repository](https://github.com/near-examples/hello-near-connector) which demonstrates all features in action. + +::: + +--- + +## Installation + +Install the `@hot-labs/near-connect` package along with its required peer dependencies: + +```bash +npm install @hot-labs/near-connect \ + @near-js/providers \ + @near-js/utils +``` + +:::tip How It Works + +Unlike traditional wallet selectors that bundle wallet code, NEAR Connect uses a **manifest-based approach**: + +1. Wallet providers register their integration scripts in a public manifest +2. The connector dynamically loads wallet scripts when users want to connect +3. Wallet code runs in **isolated sandboxed iframes** for security +4. All wallets implement a standard `NearWallet` interface +5. Communication happens via secure `postMessage` API + +This architecture eliminates the need to install individual wallet packages and ensures wallet code can be updated independently from your app. + +::: + +--- + +## Creating the Connector + +Initialize the `NearConnector` instance in your application. For a complete reference of the `NearConnector` class implementation, see the [source code on GitHub](https://github.com/azbang/hot-connector/blob/main/near-connect/src/NearConnector.ts). + +```tsx title="lib/near.ts" +// Basic connector for NEAR testnet +import { NearConnector } from "@hot-labs/near-connect"; + +connector = new NearConnector({ network: "testnet" }); +``` + +```tsx title="lib/near.ts" +// Full configuration example +import { NearConnector } from "@hot-labs/near-connect"; + +connector = new NearConnector({ + network: "testnet", // or "mainnet" + + // Optional: WalletConnect support for mobile wallets + walletConnect: { + projectId: "YOUR_PROJECT_ID", + metadata: { + name: "Your App Name", + description: "Your App Description", + url: "https://yourapp.com", + icons: ["https://yourapp.com/icon.png"], + }, + }, + + // Optional: Require specific wallet features + features: { + signMessage: true, // Only show wallets that support message signing + signTransaction: false, + }, + + // Optional: Auto-connect to previously used wallet + autoConnect: true, + + // Optional: Request access key for contract interaction + connectWithKey: { + contractId: "your-contract.testnet", + methodNames: ["method1", "method2"], + allowance: "250000000000000", // 0.25 NEAR + }, +}); +``` + +### Configuration Options + +| Option | Type | Description | +|--------|------|-------------| +| `network` | `"mainnet"` \| `"testnet"` | NEAR network to connect to | +| `walletConnect` | `object` | WalletConnect configuration for mobile wallets | +| `features` | `Partial` | Filter wallets by supported features | +| `autoConnect` | `boolean` | Auto-reconnect to last used wallet (default: `true`) | +| `connectWithKey` | `object` | Request function call access key during sign-in | +| `manifest` | `string` \| `object` | Custom wallet manifest URL or object | +| `storage` | `DataStorage` | Custom storage implementation (default: `LocalStorage`) | +| `logger` | `Logger` | Custom logger for debugging | + +--- + +## Event Listeners and Subscriptions + +The connector uses the Observer Pattern (pub/sub). Subscribe to wallet events to react to connection changes. For a complete reference of available events, see the [NearConnector source code](https://github.com/azbang/hot-connector/blob/main/near-connect/src/NearConnector.ts). + +### Available Events + +```tsx +import { connector } from "./lib/near"; + +// Listen for successful sign-in +connector.on("wallet:signIn", async({ wallet, accounts, success }) => { + const address = await wallet.getAddress(); + // or + const address2 = accounts[0].accountId; + console.log("User signed in:", address); + console.log("Wallet:", wallet.manifest.name); +}); + +// Listen for sign-out +connector.on("wallet:signOut", () => { + console.log("User signed out"); + // Clear user data, redirect, etc. +}); + +// Listen for wallet list changes (added/removed debug wallets) +connector.on("selector:walletsChanged", () => { + console.log("Available wallets changed"); + // This fires when debug wallets are registered or removed +}); + +// Listen for manifest updates +connector.on("selector:manifestUpdated", () => { + console.log("Wallet manifest has been updated"); + // This fires when the wallet manifest is refreshed or updated +}); +``` + +### Event Types + +| Event | Payload | Description | +|-------|---------|-------------| +| `wallet:signIn` | `{ wallet, accounts, success }` | Emitted when user successfully signs in | +| `wallet:signOut` | `{}` | Emitted when user signs out | +| `selector:walletsChanged` | `{}` | Emitted when debug wallets are added/removed | +| `selector:manifestUpdated` | `{}` | Emitted when wallet manifest is refreshed or updated | + +### Managing Event Listeners + +```tsx +// Listen once (auto-removes after first trigger) +connector.once("wallet:signIn", ({ accounts }) => { + console.log("First sign-in:", accounts[0].accountId); +}); + +// Remove specific listener +const handleSignOut = () => console.log("Signed out"); +connector.on("wallet:signOut", handleSignOut); +connector.off("wallet:signOut", handleSignOut); + +// Remove all listeners for an event +connector.removeAllListeners("wallet:signIn"); + +// Remove all listeners for all events +connector.removeAllListeners(); +``` + +--- + +## Connecting to a Wallet + +### Connect with Wallet Selection + +When you call `connect()` without arguments, the connector shows a modal with available wallets: + +```tsx +try { + const wallet = await connector.connect(); + console.log("Connected to:", wallet.manifest.name); + + // Get user accounts + const accounts = await wallet.getAccounts(); + console.log("Account ID:", accounts[0].accountId); +} catch (error) { + console.error("Connection failed:", error); + // User rejected or connection failed +} +``` + +### Connect to Specific Wallet + +If you know the wallet ID, connect directly: + +```tsx +// Connect to a specific wallet by ID +await connector.connect("meteor-wallet"); + +// Available wallet IDs from manifest: +// "meteor-wallet", "mynearwallet", "hot-wallet", +// "nightly-wallet", "near-mobile-wallet", etc. +``` + +### Get Connected Wallet + +Retrieve the currently connected wallet: + +```tsx +try { + const { wallet, accounts } = await connector.getConnectedWallet(); + console.log("Already connected:", accounts[0].accountId); +} catch (error) { + console.log("No wallet connected"); +} +``` + +### Disconnect Wallet + +```tsx +// Disconnect current wallet +await connector.disconnect(); + +// Or disconnect a specific wallet instance +const wallet = await connector.wallet(); +await connector.disconnect(wallet); +``` + +--- + +## Calling View Methods + +View methods are read-only and free to call. You can call them directly via RPC without user authentication: + +```tsx +import { JsonRpcProvider } from "@near-js/providers"; + +const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com" }); + +console.log("Greeting:", await provider.callFunction("hello.near-examples.testnet","get_greeting",{})); +``` + +--- + +## Calling Change Methods + +Change methods modify blockchain state and require user authentication. Get the wallet instance and call methods: + +```tsx +// Get the connected wallet +const wallet = await connector.wallet(); + +// Call a change method +const result = await wallet.signAndSendTransaction({ + receiverId: "hello.near-examples.testnet", + actions: [ + { + type: "FunctionCall", + params: { + methodName: "set_greeting", + args: { greeting: "Hello from NEAR Connect!" }, + gas: "30000000000000", // 30 TGas + deposit: "0", // No deposit + }, + }, + ], +}); + +console.log("Transaction:", result.transaction.hash); +``` + +### Send Multiple Transactions + +Execute multiple transactions atomically: + +```tsx +const wallet = await connector.wallet(); + +const results = await wallet.signAndSendTransactions({ + transactions: [ + { + receiverId: "token.near", + actions: [ + { + type: "FunctionCall", + params: { + methodName: "ft_transfer", + args: { + receiver_id: "alice.near", + amount: "1000000", + }, + gas: "30000000000000", + deposit: "1", // 1 yoctoNEAR for security + }, + }, + ], + }, + { + receiverId: "nft.near", + actions: [ + { + type: "FunctionCall", + params: { + methodName: "nft_mint", + args: { + token_id: "token-1", + receiver_id: "alice.near", + }, + gas: "30000000000000", + deposit: "10000000000000000000000", // 0.01 NEAR + }, + }, + ], + }, + ], +}); + +console.log(`Completed ${results.length} transactions`); +``` + +### Sign Messages (NEP-413) + +Sign arbitrary messages for authentication or verification: + +```tsx +const wallet = await connector.wallet(); + +const signature = await wallet.signMessage({ + message: "Please sign this message to authenticate", + recipient: "your-app.near", + nonce: Buffer.from(crypto.randomUUID()), +}); + +console.log("Signature:", signature.signature); +console.log("Public Key:", signature.publicKey); + +// Verify the signature on your backend +``` + +--- + +## Advanced Features + +### Available Wallets + +Get the list of wallets available to connect: + +```tsx +// Wait for manifest to load +await connector.whenManifestLoaded; + +// Get all wallets that match your feature requirements +const wallets = connector.availableWallets; + +wallets.forEach(wallet => { + console.log(wallet.manifest.name, wallet.manifest.id); + console.log("Features:", wallet.manifest.features); +}); +``` + +### Switch Network + +Change between mainnet and testnet: + +```tsx +// Switch to mainnet (disconnects current wallet) +await connector.switchNetwork("mainnet"); + +// Switch to testnet +await connector.switchNetwork("testnet"); +``` + +### Debug Wallets + +Register custom wallet manifests for testing: + +```tsx +// Register a debug wallet from JSON +const manifest = await connector.registerDebugWallet({ + id: "my-test-wallet", + name: "Test Wallet", + type: "sandbox", + executor: "https://localhost:3000/wallet.js", + icon: "https://localhost:3000/icon.svg", + features: { + signMessage: true, + testnet: true, + }, + permissions: { + network: true, + storage: true, + }, +}); + +// Remove debug wallet +await connector.removeDebugWallet("my-test-wallet"); +``` + +### Custom Storage + +Implement custom storage for connection persistence: + +```tsx +import { DataStorage } from "@hot-labs/near-connect"; + +class CustomStorage implements DataStorage { + async get(key: string): Promise { + return localStorage.getItem(key); + } + + async set(key: string, value: string): Promise { + localStorage.setItem(key, value); + } + + async remove(key: string): Promise { + localStorage.removeItem(key); + } +} + +const connector = new NearConnector({ + storage: new CustomStorage(), + network: "mainnet", +}); +``` + +### Injected Wallets + +NEAR Connect automatically detects browser extension wallets that implement the injection standard: + +```tsx +// In your wallet extension +class NearWallet { + manifest: { ... }; + signIn() {} + // all implementation +} + +window.addEventListener("near-selector-ready", () => { + window.dispatchEvent(new CustomEvent("near-wallet-injected", { detail: new NearWallet() })); +}); +``` + +--- + +## React Integration + +For React applications, we recommend using a custom hook to manage the connector state and lifecycle. Check out the [`useNear` hook](https://github.com/near-examples/hello-near-connector/blob/main/src/hooks/useNear.jsx) from the example repository: + +```tsx +import { useNear } from './hooks/useNear'; + +function MyComponent() { + const { + connector, + wallet, + accountId, + isConnected, + signIn, + signOut + } = useNear(); + + return ( +
+ {isConnected ? ( +
+

Connected: {accountId}

+ +
+ ) : ( + + )} +
+ ); +} +``` + +The hook handles: +- Connector initialization +- Auto-reconnect on page load +- Event listener management and cleanup +- Wallet state synchronization +- Error handling + +For a complete implementation, see the [hello-near-connector example](https://github.com/near-examples/hello-near-connector). + +--- + + + + diff --git a/website/sidebars.js b/website/sidebars.js index 072ec767447..554815eab24 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -392,6 +392,7 @@ const sidebar = { "Web Login": [ 'web3-apps/tutorials/web-login/wallet-selector', 'web3-apps/tutorials/web-login/ethereum-wallets', + 'web3-apps/tutorials/web-login/near-conector', ] }, 'tutorials/examples/frontend-multiple-contracts', From e7ac483168c5c80cc65bdc8ffede923361d9c7e7 Mon Sep 17 00:00:00 2001 From: Matias Benary Date: Tue, 28 Oct 2025 22:54:28 -0300 Subject: [PATCH 2/5] feat: add web3auth tutorial --- .../tutorials/web-login/near-conector.md | 6 +- .../tutorials/web-login/web3-auth.md | 425 ++++++++++++++++++ website/sidebars.js | 1 + 3 files changed, 427 insertions(+), 5 deletions(-) create mode 100644 docs/web3-apps/tutorials/web-login/web3-auth.md diff --git a/docs/web3-apps/tutorials/web-login/near-conector.md b/docs/web3-apps/tutorials/web-login/near-conector.md index dee7d6ecea8..17cba896c1f 100644 --- a/docs/web3-apps/tutorials/web-login/near-conector.md +++ b/docs/web3-apps/tutorials/web-login/near-conector.md @@ -504,8 +504,4 @@ The hook handles: For a complete implementation, see the [hello-near-connector example](https://github.com/near-examples/hello-near-connector). ---- - - - - +--- \ No newline at end of file diff --git a/docs/web3-apps/tutorials/web-login/web3-auth.md b/docs/web3-apps/tutorials/web-login/web3-auth.md new file mode 100644 index 00000000000..11a2a2ad990 --- /dev/null +++ b/docs/web3-apps/tutorials/web-login/web3-auth.md @@ -0,0 +1,425 @@ +--- + +id: web3-auth +title: Web3Auth Social Login Integration +sidebar_label: Web3Auth Integration +--- + +# Integrating Web3Auth with NEAR + +This tutorial demonstrates how to integrate [Web3Auth](https://web3auth.io/) into a NEAR application, enabling users to log in with social accounts (Google, Facebook, Twitter, etc.) while maintaining full blockchain functionality. + +:::tip What is Web3Auth? + +Web3Auth is a pluggable authentication infrastructure that allows users to authenticate using familiar Web2 logins (OAuth providers like Google, Facebook, etc.) while generating a cryptographic key pair for Web3 interactions. This provides a seamless onboarding experience without requiring users to manage seed phrases or private keys directly. + +::: + +## Clone and Install + +Start by cloning the repository and installing dependencies: + +```bash +git clone https://github.com/near-examples/hello-web3auth.git +cd hello-web3auth/modal +yarn install +``` + +--- + + +## Get Web3Auth Credentials + +To enable social login, you need to create a Web3Auth project and obtain a Client ID. + +### Create a Web3Auth Account + +1. Go to [Web3Auth Dashboard](https://dashboard.web3auth.io/) +2. Sign up or log in with your preferred method +3. You'll be redirected to the dashboard + +### Create a New Project + +1. Click on **"Add new project"** +2. Fill in the project details: + - **Project Name**: Choose a descriptive name (e.g., "NEAR Social Login") + - **Environment**: Select **"Sapphire Devnet"** for development + - **Chain Namespace**: Choose **"Other"** (since NEAR is not natively supported) +3. Click **"Create"** + +### Configure Your Project + +1. Once created, click on your project to open its settings.In this section you can find Client ID and Client Secret which will be used in the application configuration. +2. Change the **Select Product** to **MPC Core Kit** +3. Change **Project platform** to **Web Application** +4. Go to the **"Domains"** tab +5. Add your application's URLs: + - **Whitelist URLs**: Add `http://localhost:5173` for local development + - For production, add your deployed URL (e.g., `https://yourdomain.com`) + +--- + +## Configure Google OAuth (Optional but Recommended) + +While Web3Auth provides default OAuth providers, setting up your own Google OAuth gives you more control and branding. + +### Create a Google Cloud Project + +1. Open the [Google Cloud Console](https://console.cloud.google.com/). +2. Click Select a project → New Project (or choose an existing project). +3. Enter a project name (for example, NEAR Web3Auth) and click Create. +4. In the left sidebar, go to **APIs & Services** → **OAuth consent screen** ([direct link](https://console.cloud.google.com/auth/overview/create)). + + +### Configure OAuth Consent Screen + +1. Go to **"APIs & Services"** → **"OAuth consent screen"** +2. Choose **"External"** (unless you have a Google Workspace) +3. Fill in the required information: + - **App name**: Your application name + - **User support email**: Your email + - **Developer contact**: Your email +4. Click **"Save and Continue"** +5. On the **Scopes** page, click **"Save and Continue"** (default scopes are fine) +6. On the **Test users** page, add your email for testing +7. Click **"Save and Continue"** + +### Create OAuth Credentials + +1. Go to **"APIs & Services"** → **"Credentials"** +2. Click **"+ Create Credentials"** → **"OAuth client ID"** ([direct link](https://console.cloud.google.com/auth/clients/create)) +3. Select **"Web application"** +4. Configure: + - **Name**: Choose a descriptive name + - **Authorized JavaScript origins**: + - Add `http://localhost:5173` (for development) + - Add your production domain (when ready) + - **Authorized redirect URIs**: + - Add `https://auth.web3auth.io/auth` + - This is Web3Auth's callback URL +5. Click **"Create"** +6. Copy the **Client ID** and **Client Secret** + +### Add Google Credentials to Web3Auth + +1. Return to [Web3Auth Dashboard](https://dashboard.web3auth.io/) +2. Go to the **Authentication** in left sidebar +3. Click on **Google** in social Logins +4. Click on **Add connection** and fill in the following details: + - **Auth Connection ID***: Your desired connection ID (e.g., `near-login`),This use for verifier value in application configuration. + - **Enter Google Client ID***: Your Google OAuth Client Secret from previous step.(e.g., `17426988624-32m2gh1o1n5qve6govq04ue91sioruk7WWapps.googleusercontent.com`) + +--- + +## Configure Your Application + +Now that you have your Web3Auth Client ID, update your application configuration. + +### Update the Config File + +Open `src/config.js` and replace the `clientId` with your own: + +```javascript +// Web3Auth Config +import { CHAIN_NAMESPACES, WEB3AUTH_NETWORK } from '@web3auth/base' +import { CommonPrivateKeyProvider } from '@web3auth/base-provider' + +const chainConfig = { + chainNamespace: CHAIN_NAMESPACES.OTHER, + chainId: 'near:testnet', + rpcTarget: 'https://rpc.testnet.near.org', + displayName: 'NEAR Testnet', + blockExplorerUrl: 'https://nearblocks.io', + ticker: 'NEAR', + tickerName: 'NEAR', +} + +const privateKeyProvider = new CommonPrivateKeyProvider({ + config: { chainConfig } +}) + +export const web3AuthContextConfig = { + web3AuthOptions: { + clientId: 'YOUR_WEB3AUTH_CLIENT_ID_HERE', // Replace with your Client ID + web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_DEVNET, + chainConfig, + privateKeyProvider, + } +} + +// NEAR Config +const contractPerNetwork = { + mainnet: 'hello.near-examples.near', + testnet: 'hello.near-examples.testnet', +} + +export const NetworkId = 'testnet' +export const providerUrl = 'https://test.rpc.fastnear.com' +export const HelloNearContract = contractPerNetwork[NetworkId] +``` + +--- + +## Run the Application + +Start the development server: + +```bash +yarn run dev +``` + +Open your browser and navigate to `http://localhost:5173`. + +### Testing the Integration + +1. Click the **"Login"** button in the navigation bar +2. The Web3Auth modal will appear with various login options +3. Choose **"Continue with Google"** (or another provider) +4. Complete the authentication flow +5. Once logged in, you'll see: + - Your derived NEAR account ID in the navigation + - Your NEAR balance + - A logout button with your email/name + +--- + +## Understanding the Implementation + +### Architecture Overview + +This integration uses two main components: + +1. **Web3Auth Modal** (`@web3auth/modal-react-hooks`): Provides the UI and authentication flow +2. **NEAR Integration** (custom provider): Derives NEAR keys from Web3Auth and manages blockchain interactions + +### Key Components + +#### 1. Web3Auth Provider (`App.jsx`) + +```jsx +import { Web3AuthProvider } from "@web3auth/modal-react-hooks"; +import { web3AuthContextConfig } from './config'; + +function App() { + return ( + + + {/* Your app components */} + + + ) +} +``` + +The `Web3AuthProvider` wraps your application and provides authentication state. + +#### 2. NEAR Context Provider (`src/context/provider.jsx`) + +This custom provider bridges Web3Auth and NEAR: + +```jsx +// near api js +import { JsonRpcProvider } from '@near-js/providers' +import { Account } from '@near-js/accounts' + +// utils +import { base58 } from '@scure/base' + +// config +import { useEffect } from 'react' +import { useState } from 'react' +import { NearContext } from './useNear' +import { providerUrl } from '../config' +import { useWeb3Auth } from '@web3auth/modal-react-hooks' +import { KeyPair } from '@near-js/crypto' +import { KeyPairSigner } from '@near-js/signers' +import { getED25519Key } from '@web3auth/base-provider' + +// Provider +const provider = new JsonRpcProvider({ url: providerUrl }) + +// eslint-disable-next-line react/prop-types +export function NEARxWeb3Auth({ children }) { + const [walletId, setWalletId] = useState(null) + const [nearAccount, setNearAccount] = useState(null) + const [loading, setLoading] = useState(true) + + // Get Web3Auth Modal state - this provides the authenticated session + const { isConnected, authenticateUser, userInfo, provider: web3authProvider } = useWeb3Auth() + + // Sync MPC Core Kit with Web3Auth Modal authentication + useEffect(() => { + const syncAuth = async () => { + if (!isConnected) { + setWalletId(null) + setNearAccount(null) + setLoading(false) + return + } + + if (!web3authProvider) return; + + const privateKey = await web3authProvider.request({ method: 'private_key' }) + const privateKeyEd25519 = getED25519Key(privateKey).sk // Already a Uint8Array + + // Create keypair and derive account ID from public key + const keyPair = KeyPair.fromString(`ed25519:${base58.encode(privateKeyEd25519)}`); + const signer = new KeyPairSigner(keyPair); + const accountId = Array.from(keyPair.getPublicKey().data).map(b => b.toString(16).padStart(2, '0')).join('') + const account = new Account(accountId, provider, signer); + + setWalletId(account.accountId) + setNearAccount(account) + setLoading(false) + } + + syncAuth() + }, [isConnected, authenticateUser, userInfo, web3authProvider]) + + return ( + + {children} + + ) +} +``` + +#### 3. Using the Context (`src/components/navigation.jsx`) + +Components can access both Web3Auth and NEAR state: + +```jsx +import { useEffect, useState } from 'react' + +import NearLogo from '@/assets/near-logo.svg' +import { Link } from 'react-router' +import styles from '@/styles/app.module.css' + +import { useNEARxWeb3Auth } from '../context/useNear' +import { useWeb3Auth } from '@web3auth/modal-react-hooks' +import { NEAR } from '@near-js/tokens' + +export const Navigation = () => { + const [action, setAction] = useState(() => {}) + const [label, setLabel] = useState('Loading...') + const [balance, setBalance] = useState(0) + + // Web3Auth Modal for easy login UI + const { connect, logout, isConnected, userInfo } = useWeb3Auth() + + // MPC Core Kit for NEAR transaction signing + const { walletId, nearAccount, loading } = useNEARxWeb3Auth() + + useEffect(() => { + if (loading) { + setLabel('Loading...') + return + } + + if (isConnected) { + const userId = userInfo?.email || userInfo?.name || 'User' + setAction(() => logout) + setLabel(`Logout ${userId}`) + } else { + setAction(() => connect) + setLabel('Login') + setBalance(null) + } + }, [isConnected, walletId, loading, userInfo, logout, connect]) + + useEffect(() => { + if (walletId && nearAccount) { + nearAccount + .getBalance() + .then((b) => setBalance(Number(NEAR.toDecimal(b)).toFixed(2))) + .catch(() => setBalance(0)) + } + }, [walletId, nearAccount]) +} +``` + +### How It Works + +1. **User Authentication**: User logs in via Web3Auth (Google, etc.) +2. **Key Derivation**: Web3Auth generates a private key based on the user's social login +3. **NEAR Key Conversion**: The private key is converted to NEAR's ED25519 format +4. **Account ID Generation**: A deterministic account ID is derived from the public key +5. **Blockchain Interaction**: The NEAR account instance can now sign transactions + +:::warning NEAR Provider Implementation + +After a user logs in, they receive a provider from the Embedded Wallets SDK. However, there is no native provider for NEAR, so we use the private key to make RPC calls directly. This is why we extract the private key from Web3Auth's provider and create a custom NEAR account instance using `@near-js/accounts` and `@near-js/providers`. + +::: + +--- + +## Interacting with Smart Contracts + +Once authenticated, you can interact with NEAR smart contracts. Here's an example from `src/pages/hello_near.jsx`: + +```jsx +import { useEffect, useState } from 'react' + +import { Cards } from '@/components/cards' +import styles from '@/styles/app.module.css' + +import { HelloNearContract } from '@/config' +import { useNEARxWeb3Auth } from '../context/useNear' +import { useCallback } from 'react' + +// Contract that the app will interact with +const CONTRACT = HelloNearContract + +export default function HelloNear() { + const [greeting, setGreeting] = useState('loading...') + const [newGreeting, setNewGreeting] = useState('loading...') + const [loggedIn, setLoggedIn] = useState(false) + const [showSpinner, setShowSpinner] = useState(false) + + const { provider, nearAccount, walletId } = useNEARxWeb3Auth() + + const fetchGreeting = useCallback(async () => { + const greeting = await provider.callFunction(CONTRACT, 'get_greeting', {}) + setGreeting(greeting) + }, [provider]) + + const saveGreeting = async () => { + nearAccount + .callFunction({ + contractId: CONTRACT, + methodName: 'set_greeting', + args: { greeting: newGreeting }, + }) + .catch((e) => { + alert( + `Error, did you deposit any NEAR Ⓝ? You can get some at https://dev.near.org/faucet` + ) + console.log(`Error saving greeting: ${e.message}`) + fetchGreeting() + }) + + setShowSpinner(true) + await new Promise((resolve) => setTimeout(resolve, 300)) + setGreeting(newGreeting) + setShowSpinner(false) + } + + useEffect(() => { + setLoggedIn(!!walletId) + }, [walletId]) + + useEffect(() => { + fetchGreeting() + }, [fetchGreeting]) +} +``` + +--- \ No newline at end of file diff --git a/website/sidebars.js b/website/sidebars.js index 554815eab24..5be920cdf51 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -393,6 +393,7 @@ const sidebar = { 'web3-apps/tutorials/web-login/wallet-selector', 'web3-apps/tutorials/web-login/ethereum-wallets', 'web3-apps/tutorials/web-login/near-conector', + 'web3-apps/tutorials/web-login/web3-auth', ] }, 'tutorials/examples/frontend-multiple-contracts', From 76e854cc17e3e2d3d7ad22d85ca5b7bead8d58a2 Mon Sep 17 00:00:00 2001 From: Matias Benary Date: Tue, 28 Oct 2025 23:42:22 -0300 Subject: [PATCH 3/5] merge --- docs/web3-apps/concepts/web-login.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/web3-apps/concepts/web-login.md b/docs/web3-apps/concepts/web-login.md index bdeb6cdfcfc..af74bf0ccd4 100644 --- a/docs/web3-apps/concepts/web-login.md +++ b/docs/web3-apps/concepts/web-login.md @@ -50,11 +50,7 @@ Considered a successor to the wallet selector, the [NEAR Connector](https://gith :::tip -<<<<<<< HEAD -You can learn how to integrate the wallet selector into your app in our [NEAR Connector](../tutorials/web-login/near-conector.md) guide. -======= You can learn how to integrate the `near connector` into your app in the [NEAR Connector tutorial](../tutorials/web-login/near-connector.md). ->>>>>>> master ::: From bace50c89457eabb2007b70a21e2df06aa72f72a Mon Sep 17 00:00:00 2001 From: Guille Date: Thu, 30 Oct 2025 15:58:42 +0100 Subject: [PATCH 4/5] Delete docs/web3-apps/tutorials/web-login/near-conector.md --- .../tutorials/web-login/near-conector.md | 507 ------------------ 1 file changed, 507 deletions(-) delete mode 100644 docs/web3-apps/tutorials/web-login/near-conector.md diff --git a/docs/web3-apps/tutorials/web-login/near-conector.md b/docs/web3-apps/tutorials/web-login/near-conector.md deleted file mode 100644 index 17cba896c1f..00000000000 --- a/docs/web3-apps/tutorials/web-login/near-conector.md +++ /dev/null @@ -1,507 +0,0 @@ ---- -id: near-conector -title: NEAR Connect Tutorial -description: "Connect users to NEAR wallets with a secure, sandbox-based connector library" ---- -import {CodeTabs, Language, Github} from "@site/src/components/UI/Codetabs" -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -The `@hot-labs/near-connect` library provides a secure, zero-dependency wallet connector for NEAR blockchain with a unique sandbox-based architecture. Unlike traditional wallet selectors, it offers a dynamic manifest system that allows wallets to be added and updated without requiring developers to update their dependencies. - -![Preview](https://github.com/user-attachments/assets/c4422057-38bb-4cd9-8bd0-568e29f46280) - -:::tip Why NEAR Connect? - -- **Secure Execution**: Wallet scripts run in isolated sandboxed iframes for maximum security -- **Dynamic Wallets**: Wallets are loaded from a manifest and can be updated without code changes -- **Zero Dependencies**: Lightweight library with no external dependencies -- **Automatic Detection**: Supports both injected wallets (extensions) and manifest-based wallets - -::: - -:::info - -Check other options to let users login into your application and use NEAR accounts in the [Web Login](../../concepts/web-login.md) section - -::: - -:::tip Working Example - -For a complete working example with React, check out the [hello-near-connector repository](https://github.com/near-examples/hello-near-connector) which demonstrates all features in action. - -::: - ---- - -## Installation - -Install the `@hot-labs/near-connect` package along with its required peer dependencies: - -```bash -npm install @hot-labs/near-connect \ - @near-js/providers \ - @near-js/utils -``` - -:::tip How It Works - -Unlike traditional wallet selectors that bundle wallet code, NEAR Connect uses a **manifest-based approach**: - -1. Wallet providers register their integration scripts in a public manifest -2. The connector dynamically loads wallet scripts when users want to connect -3. Wallet code runs in **isolated sandboxed iframes** for security -4. All wallets implement a standard `NearWallet` interface -5. Communication happens via secure `postMessage` API - -This architecture eliminates the need to install individual wallet packages and ensures wallet code can be updated independently from your app. - -::: - ---- - -## Creating the Connector - -Initialize the `NearConnector` instance in your application. For a complete reference of the `NearConnector` class implementation, see the [source code on GitHub](https://github.com/azbang/hot-connector/blob/main/near-connect/src/NearConnector.ts). - -```tsx title="lib/near.ts" -// Basic connector for NEAR testnet -import { NearConnector } from "@hot-labs/near-connect"; - -connector = new NearConnector({ network: "testnet" }); -``` - -```tsx title="lib/near.ts" -// Full configuration example -import { NearConnector } from "@hot-labs/near-connect"; - -connector = new NearConnector({ - network: "testnet", // or "mainnet" - - // Optional: WalletConnect support for mobile wallets - walletConnect: { - projectId: "YOUR_PROJECT_ID", - metadata: { - name: "Your App Name", - description: "Your App Description", - url: "https://yourapp.com", - icons: ["https://yourapp.com/icon.png"], - }, - }, - - // Optional: Require specific wallet features - features: { - signMessage: true, // Only show wallets that support message signing - signTransaction: false, - }, - - // Optional: Auto-connect to previously used wallet - autoConnect: true, - - // Optional: Request access key for contract interaction - connectWithKey: { - contractId: "your-contract.testnet", - methodNames: ["method1", "method2"], - allowance: "250000000000000", // 0.25 NEAR - }, -}); -``` - -### Configuration Options - -| Option | Type | Description | -|--------|------|-------------| -| `network` | `"mainnet"` \| `"testnet"` | NEAR network to connect to | -| `walletConnect` | `object` | WalletConnect configuration for mobile wallets | -| `features` | `Partial` | Filter wallets by supported features | -| `autoConnect` | `boolean` | Auto-reconnect to last used wallet (default: `true`) | -| `connectWithKey` | `object` | Request function call access key during sign-in | -| `manifest` | `string` \| `object` | Custom wallet manifest URL or object | -| `storage` | `DataStorage` | Custom storage implementation (default: `LocalStorage`) | -| `logger` | `Logger` | Custom logger for debugging | - ---- - -## Event Listeners and Subscriptions - -The connector uses the Observer Pattern (pub/sub). Subscribe to wallet events to react to connection changes. For a complete reference of available events, see the [NearConnector source code](https://github.com/azbang/hot-connector/blob/main/near-connect/src/NearConnector.ts). - -### Available Events - -```tsx -import { connector } from "./lib/near"; - -// Listen for successful sign-in -connector.on("wallet:signIn", async({ wallet, accounts, success }) => { - const address = await wallet.getAddress(); - // or - const address2 = accounts[0].accountId; - console.log("User signed in:", address); - console.log("Wallet:", wallet.manifest.name); -}); - -// Listen for sign-out -connector.on("wallet:signOut", () => { - console.log("User signed out"); - // Clear user data, redirect, etc. -}); - -// Listen for wallet list changes (added/removed debug wallets) -connector.on("selector:walletsChanged", () => { - console.log("Available wallets changed"); - // This fires when debug wallets are registered or removed -}); - -// Listen for manifest updates -connector.on("selector:manifestUpdated", () => { - console.log("Wallet manifest has been updated"); - // This fires when the wallet manifest is refreshed or updated -}); -``` - -### Event Types - -| Event | Payload | Description | -|-------|---------|-------------| -| `wallet:signIn` | `{ wallet, accounts, success }` | Emitted when user successfully signs in | -| `wallet:signOut` | `{}` | Emitted when user signs out | -| `selector:walletsChanged` | `{}` | Emitted when debug wallets are added/removed | -| `selector:manifestUpdated` | `{}` | Emitted when wallet manifest is refreshed or updated | - -### Managing Event Listeners - -```tsx -// Listen once (auto-removes after first trigger) -connector.once("wallet:signIn", ({ accounts }) => { - console.log("First sign-in:", accounts[0].accountId); -}); - -// Remove specific listener -const handleSignOut = () => console.log("Signed out"); -connector.on("wallet:signOut", handleSignOut); -connector.off("wallet:signOut", handleSignOut); - -// Remove all listeners for an event -connector.removeAllListeners("wallet:signIn"); - -// Remove all listeners for all events -connector.removeAllListeners(); -``` - ---- - -## Connecting to a Wallet - -### Connect with Wallet Selection - -When you call `connect()` without arguments, the connector shows a modal with available wallets: - -```tsx -try { - const wallet = await connector.connect(); - console.log("Connected to:", wallet.manifest.name); - - // Get user accounts - const accounts = await wallet.getAccounts(); - console.log("Account ID:", accounts[0].accountId); -} catch (error) { - console.error("Connection failed:", error); - // User rejected or connection failed -} -``` - -### Connect to Specific Wallet - -If you know the wallet ID, connect directly: - -```tsx -// Connect to a specific wallet by ID -await connector.connect("meteor-wallet"); - -// Available wallet IDs from manifest: -// "meteor-wallet", "mynearwallet", "hot-wallet", -// "nightly-wallet", "near-mobile-wallet", etc. -``` - -### Get Connected Wallet - -Retrieve the currently connected wallet: - -```tsx -try { - const { wallet, accounts } = await connector.getConnectedWallet(); - console.log("Already connected:", accounts[0].accountId); -} catch (error) { - console.log("No wallet connected"); -} -``` - -### Disconnect Wallet - -```tsx -// Disconnect current wallet -await connector.disconnect(); - -// Or disconnect a specific wallet instance -const wallet = await connector.wallet(); -await connector.disconnect(wallet); -``` - ---- - -## Calling View Methods - -View methods are read-only and free to call. You can call them directly via RPC without user authentication: - -```tsx -import { JsonRpcProvider } from "@near-js/providers"; - -const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com" }); - -console.log("Greeting:", await provider.callFunction("hello.near-examples.testnet","get_greeting",{})); -``` - ---- - -## Calling Change Methods - -Change methods modify blockchain state and require user authentication. Get the wallet instance and call methods: - -```tsx -// Get the connected wallet -const wallet = await connector.wallet(); - -// Call a change method -const result = await wallet.signAndSendTransaction({ - receiverId: "hello.near-examples.testnet", - actions: [ - { - type: "FunctionCall", - params: { - methodName: "set_greeting", - args: { greeting: "Hello from NEAR Connect!" }, - gas: "30000000000000", // 30 TGas - deposit: "0", // No deposit - }, - }, - ], -}); - -console.log("Transaction:", result.transaction.hash); -``` - -### Send Multiple Transactions - -Execute multiple transactions atomically: - -```tsx -const wallet = await connector.wallet(); - -const results = await wallet.signAndSendTransactions({ - transactions: [ - { - receiverId: "token.near", - actions: [ - { - type: "FunctionCall", - params: { - methodName: "ft_transfer", - args: { - receiver_id: "alice.near", - amount: "1000000", - }, - gas: "30000000000000", - deposit: "1", // 1 yoctoNEAR for security - }, - }, - ], - }, - { - receiverId: "nft.near", - actions: [ - { - type: "FunctionCall", - params: { - methodName: "nft_mint", - args: { - token_id: "token-1", - receiver_id: "alice.near", - }, - gas: "30000000000000", - deposit: "10000000000000000000000", // 0.01 NEAR - }, - }, - ], - }, - ], -}); - -console.log(`Completed ${results.length} transactions`); -``` - -### Sign Messages (NEP-413) - -Sign arbitrary messages for authentication or verification: - -```tsx -const wallet = await connector.wallet(); - -const signature = await wallet.signMessage({ - message: "Please sign this message to authenticate", - recipient: "your-app.near", - nonce: Buffer.from(crypto.randomUUID()), -}); - -console.log("Signature:", signature.signature); -console.log("Public Key:", signature.publicKey); - -// Verify the signature on your backend -``` - ---- - -## Advanced Features - -### Available Wallets - -Get the list of wallets available to connect: - -```tsx -// Wait for manifest to load -await connector.whenManifestLoaded; - -// Get all wallets that match your feature requirements -const wallets = connector.availableWallets; - -wallets.forEach(wallet => { - console.log(wallet.manifest.name, wallet.manifest.id); - console.log("Features:", wallet.manifest.features); -}); -``` - -### Switch Network - -Change between mainnet and testnet: - -```tsx -// Switch to mainnet (disconnects current wallet) -await connector.switchNetwork("mainnet"); - -// Switch to testnet -await connector.switchNetwork("testnet"); -``` - -### Debug Wallets - -Register custom wallet manifests for testing: - -```tsx -// Register a debug wallet from JSON -const manifest = await connector.registerDebugWallet({ - id: "my-test-wallet", - name: "Test Wallet", - type: "sandbox", - executor: "https://localhost:3000/wallet.js", - icon: "https://localhost:3000/icon.svg", - features: { - signMessage: true, - testnet: true, - }, - permissions: { - network: true, - storage: true, - }, -}); - -// Remove debug wallet -await connector.removeDebugWallet("my-test-wallet"); -``` - -### Custom Storage - -Implement custom storage for connection persistence: - -```tsx -import { DataStorage } from "@hot-labs/near-connect"; - -class CustomStorage implements DataStorage { - async get(key: string): Promise { - return localStorage.getItem(key); - } - - async set(key: string, value: string): Promise { - localStorage.setItem(key, value); - } - - async remove(key: string): Promise { - localStorage.removeItem(key); - } -} - -const connector = new NearConnector({ - storage: new CustomStorage(), - network: "mainnet", -}); -``` - -### Injected Wallets - -NEAR Connect automatically detects browser extension wallets that implement the injection standard: - -```tsx -// In your wallet extension -class NearWallet { - manifest: { ... }; - signIn() {} - // all implementation -} - -window.addEventListener("near-selector-ready", () => { - window.dispatchEvent(new CustomEvent("near-wallet-injected", { detail: new NearWallet() })); -}); -``` - ---- - -## React Integration - -For React applications, we recommend using a custom hook to manage the connector state and lifecycle. Check out the [`useNear` hook](https://github.com/near-examples/hello-near-connector/blob/main/src/hooks/useNear.jsx) from the example repository: - -```tsx -import { useNear } from './hooks/useNear'; - -function MyComponent() { - const { - connector, - wallet, - accountId, - isConnected, - signIn, - signOut - } = useNear(); - - return ( -
- {isConnected ? ( -
-

Connected: {accountId}

- -
- ) : ( - - )} -
- ); -} -``` - -The hook handles: -- Connector initialization -- Auto-reconnect on page load -- Event listener management and cleanup -- Wallet state synchronization -- Error handling - -For a complete implementation, see the [hello-near-connector example](https://github.com/near-examples/hello-near-connector). - ---- \ No newline at end of file From 1ee5de506f58bca818d66e812aa7412af17fcc9b Mon Sep 17 00:00:00 2001 From: Guille Date: Thu, 30 Oct 2025 15:59:02 +0100 Subject: [PATCH 5/5] Update website/sidebars.js --- website/sidebars.js | 1 - 1 file changed, 1 deletion(-) diff --git a/website/sidebars.js b/website/sidebars.js index 3738f6ac461..4e34795435b 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -396,7 +396,6 @@ const sidebar = { 'web3-apps/tutorials/web-login/near-connector', 'web3-apps/tutorials/web-login/wallet-selector', 'web3-apps/tutorials/web-login/ethereum-wallets', - 'web3-apps/tutorials/web-login/near-conector', 'web3-apps/tutorials/web-login/web3-auth', ] },