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>;
}