Lock Open Source • MIT Licensed
SignLess Logo

No-Sign Authentication
For Solana dApps

Verify wallet ownership through blockchain transactions. No signature popups, no signing required. Pure transaction-based authentication that's easy to integrate.

< 2s
Auth Time
100%
Decentralized
Open
Source

How It Works

Simple 3-step authentication flow

01

Session Created

Backend creates a unique session with verification details when user initiates authentication

// Backend creates session
const session = {
  id: uuid(),
  walletAddress: request.walletAddress,
  receiverAddress: process.env.RECEIVER,
  expectedAmount: 0.00001,
  status: 'pending',
  expiresAt: Date.now() + 900000
};

sessions.set(sessionId, session);
return session;
02

Send Transaction

User sends a micro verification transaction (0.00001 SOL ≈ $0.002) from their wallet to prove ownership

User Wallet
→ 0.00001 SOL →
Verification Address
03

On-Chain Verification

Backend automatically monitors blockchain and detects matching transaction

// Backend polls blockchain for matching tx
const signatures = await connection
  .getSignaturesForAddress(receiverPubkey);

for (const sig of signatures) {
  const tx = await connection.getTransaction(sig);
  
  // Check sender + amount match
  if (tx.sender === session.walletAddress &&
      tx.amount === session.expectedAmount) {
    
    session.status = 'verified';
    return { authenticated: true };
  }
}

Why Choose SignLess?

A non-profit, open-source mission to eliminate wallet drains on Solana. We're building this to protect the community, funded entirely by token creator rewards. No fees, no profit, just safer Web3 for everyone.

No Signing Required

No Signing Required

No blind signature approvals that drain wallets. Users see exactly what they're sending—attackers can't hide malicious transactions.

Lightning Fast

Lightning Fast

Authenticate in under 2 seconds on Solana's high-speed blockchain. No waiting, just seamless auth.

100% Decentralized

100% Decentralized

No central auth servers. Verification happens on-chain, ensuring true Web3 principles.

Easy Integration

Easy Integration

Simple REST API. Works with any stack. Get up and running in minutes, not days.

Self-Hostable

Self-Hostable

Run your own instance or use our hosted service. You control your infrastructure.

Open Source

Open Source

MIT licensed. Full transparency. Contribute, fork, or customize for your needs.

Quick Integration - Front-End

Implementation examples to get you started. Full setup guide available on GitHub.

Vanilla JavaScript
// Step 1: Get wallet address from user
const walletAddress = prompt('Enter your Solana wallet address:');

// Step 2: Initiate authentication
const response = await fetch('https://your-api.com/api/auth/initiate', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ walletAddress })
});

const data = await response.json();

// Step 3: Display instructions to user
alert(`Please send ${data.expectedAmount} SOL to: ${data.receiverAddress}`);
// Show these details in your UI with copy buttons

// Step 4: Poll for verification
const checkAuth = async () => {
  const statusRes = await fetch(
    `https://your-api.com/api/auth/status/${data.sessionId}`
  );
  const status = await statusRes.json();
  
  if (status.status === 'verified') {
    console.log('✅ Authenticated!', status);
    localStorage.setItem('sessionId', data.sessionId);
    // User is authenticated - proceed to app
  } else if (status.status === 'pending') {
    // Still waiting - check again in 5 seconds
    setTimeout(checkAuth, 5000);
  }
};

checkAuth(); // Start polling
React Hook
import { useState, useEffect } from 'react';

function MyApp() {
  const [walletAddress, setWalletAddress] = useState('');
  const [authDetails, setAuthDetails] = useState(null);
  const [authenticated, setAuthenticated] = useState(false);
  const [polling, setPolling] = useState(false);

  // Poll for authentication status
  useEffect(() => {
    if (!polling || !authDetails) return;

    const interval = setInterval(async () => {
      const res = await fetch(
        `https://your-api.com/api/auth/status/${authDetails.sessionId}`
      );
      const status = await res.json();

      if (status.status === 'verified') {
        setAuthenticated(true);
        setPolling(false);
        localStorage.setItem('sessionId', authDetails.sessionId);
      }
    }, 5000); // Poll every 5 seconds

    return () => clearInterval(interval);
  }, [polling, authDetails]);

  const startAuth = async () => {
    // 1. Initiate authentication
    const res = await fetch('https://your-api.com/api/auth/initiate', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ walletAddress })
    });
    const data = await res.json();

    // 2. Show user what to send (they send manually from wallet app)
    setAuthDetails({ 
      sessionId: data.sessionId, 
      receiverAddress: data.receiverAddress, 
      expectedAmount: data.expectedAmount 
    });
    setPolling(true);
  };

  return (
    <div>
      {!authDetails ? (
        <>
          <input
            value={walletAddress}
            onChange={(e) => setWalletAddress(e.target.value)}
            placeholder="Enter wallet address"
          />
          <button onClick={startAuth}>Start Auth</button>
        </>
      ) : !authenticated ? (
        <div>
          <p>Open your wallet app and send:</p>
          <p>Amount: {authDetails.expectedAmount} SOL</p>
          <p>To: <code>{authDetails.receiverAddress}</code></p>
          <p>Waiting for transaction...</p>
        </div>
      ) : (
        <p>✅ Authenticated!</p>
      )}
    </div>
  );
}
Node.js Middleware
const express = require('express');
const axios = require('axios');

const API_URL = 'https://your-api.com';

// Middleware to protect routes
async function requireAuth(req, res, next) {
  const sessionId = req.headers['x-session-id'];
  
  if (!sessionId) {
    return res.status(401).json({ error: 'No session ID provided' });
  }
  
  try {
    const response = await axios.get(
      `${API_URL}/api/auth/status/${sessionId}`
    );
    
    if (response.data.status === 'verified') {
      req.user = { 
        walletAddress: response.data.walletAddress,
        sessionId: sessionId 
      };
      next();
    } else {
      res.status(401).json({ error: 'Not authenticated' });
    }
  } catch (error) {
    res.status(401).json({ error: 'Authentication check failed' });
  }
}

// Use on protected routes
const app = express();

app.get('/api/protected', requireAuth, (req, res) => {
  res.json({ 
    message: 'Welcome!',
    wallet: req.user.walletAddress 
  });
});

app.listen(3000);
Python/Flask Decorator
import requests
from functools import wraps
from flask import Flask, request, jsonify

app = Flask(__name__)
API_URL = 'https://your-api.com'

def require_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        session_id = request.headers.get('X-Session-Id')
        
        if not session_id:
            return jsonify({'error': 'No session ID provided'}), 401
        
        try:
            response = requests.get(
                f'{API_URL}/api/auth/status/{session_id}',
                timeout=5
            )
            data = response.json()
            
            if data.get('status') == 'verified':
                request.user = {
                    'walletAddress': data['walletAddress'],
                    'sessionId': session_id
                }
                return f(*args, **kwargs)
            else:
                return jsonify({'error': 'Not authenticated'}), 401
                
        except requests.RequestException as e:
            return jsonify({'error': 'Authentication check failed'}), 401
    
    return decorated

@app.route('/api/protected')
@require_auth
def protected_route():
    return jsonify({
        'message': 'Welcome!',
        'wallet': request.user['walletAddress']
    })

if __name__ == '__main__':
    app.run()

100% Open Source

Free forever. MIT licensed. Built for the community.

Free Forever

Community Edition

$0 forever

Complete authentication system. Deploy anywhere. Modify as needed. No strings attached.

  • Unlimited authentications
  • Full source code access
  • Self-hosted - you control everything
  • Deploy to Railway, Vercel, AWS, anywhere
  • MIT Licensed - commercial use allowed
  • Complete documentation & examples
View on GitHub
💡 Quick Start:
Clone repo → Deploy to Railway ($5/mo) → Live in 5 minutes

Support SignLess

Help us keep the lights on

SignLess Token

SignLess is a nonprofit open-source project. We rely on creator rewards from our Solana token to maintain the infrastructure, keep the demo website online, and continue development.

100% open source and free to use forever
Creator rewards fund server hosting and development
No fees, no premium tiers, no paywalls
Community-driven development and support

By trading the SignLess token, you help us cover costs for Railway hosting, Solana RPC services, domain registration, and developer time.

Token Address: CDD7ZnFXaGH7h9xy2VYUaupsshpgfFQTzqLPcLi1pump

Note: This is not financial advice. The token is purely a way to support the project through creator rewards. SignLess will always remain free and open source.

Ready to Build?

Get started in 5 minutes with our quick start guide

View Documentation