Skip to content

Ed25519

Preserve standard seed-derived Ed25519 identity while splitting wallet signing authority.

Ed25519

Seams preserves the standard seed-derived Ed25519 identity so an explicitly exported key reproduces the registered public key:

text
d = LE32(y_client + y_server mod 2^256)
h = SHA-512(d)
a = clamp(h[0..32]) mod l

The approved lifecycle protocol computes this relation through actively secure Streaming Yao A/B. Deriver A and Deriver B receive separate client and server contributions. Neither role learns the joined d, a, client signing output, or server signing output.

Lifecycle and signing

Registration, recovery, signing-share refresh, SigningWorker activation, authorized export, and new-recipient share provisioning use the lifecycle protocol. Normal transaction, message, and delegate-action signing uses the already-activated threshold session:

text
Client -> Router -> SigningWorker -> Router -> Client

Normal signing performs zero Yao work and makes zero Deriver calls. A presign pool hit takes one client-to-Router request and response. A pool miss uses a prepare request followed by the final signing request.

Export invariant

Export is a distinct, freshly authorized ceremony. The client receives two recipient-encrypted seed shares, reconstructs d, derives its Ed25519 public key, and requires equality with the registered public key before returning the exported key.

Ordinary activation circuits have no seed-output branch. Recovery cannot reuse the export request shape.

Sign a NEAR transaction

Application code supplies the exact wallet session, NEAR account, receiver, and actions. The SDK routes the request through the active Ed25519 signing lane.

Runnable React and TypeScript example
tsx
import { functionCall, logWalletEvents, TxExecutionStatus, useWallet } from '@seams/wallet/react';

export function SetGreetingButton() {
  // `near` is null when nobody is signed in, and when the signed-in wallet has
  // no NEAR account yet. One check covers both; read `status` to tell them apart.
  const { near } = useWallet();
  if (!near) return null;

  const onSign = async (): Promise<void> => {
    // Each request opens the wallet confirmation, where the user approves this
    // transaction with the wallet's auth method.
    await near.signAndSendTransaction({
      receiverId: 'guest-book.testnet',
      actions: [functionCall({ method: 'set_greeting', args: { greeting: 'Hello from Seams' } })],
      options: {
        waitUntil: TxExecutionStatus.EXECUTED_OPTIMISTIC,
        onEvent: logWalletEvents(),
      },
    });
  };

  return <button onClick={() => void onSign()}>Sign transaction</button>;
}