Note: ⚠️ Protocol derived keypairs differ.
npm install @oxc-wallet/protocolsNote: ⚠️ Protocol derived keypairs differ.
1. Issuer sends identifier to backend
2. Backend signs a provenance chain
3. Backend signature hash is returned to issuer
4. Issuer signs a provenance chain
5. Issuer signature hash is used as identifier's private key
Optional: To protect user identity, Issuer could hash internal user identifier with adjacent salt.
```
protocol = 'loophole'
h1 = userEmail OR keccak256(concat(userEmail, userSalt))
h2 = keccak256(sign(concat(protocol, backendAddress, issuerAddress, h1), backendSK))
sk = keccak256(sign(concat(protocol, backendAddress, issuerAddress, h2), issuerSK))
`ts
import { Peer } from "@oxc-wallet/protocols";
import { createWalletClient, http } from "viem";
const issuer = new Peer("0x....");
const remote = await Peer.remote("https://wallet.oxc.sh", "loophole");
const account = await issuer.derive("dev@0xcore.com", remote);
const wallet = createWalletClient({
account,
chain,
transport: http(),
});
const signature = await wallet.signMessage({ message: "Hello World" });
`
1. Issuer sends identifier to backendidentifier's
2. Backend signs a provenance chain
3. Backend signature hash is used as private key
4. Identifier address is returned to issuer
5. Issuer creates a JSON-RPC account with signing delegated to backend
``
protocol = 'oxford'
id = userEmail
sk = keccak256(sign(concat(protocol, backendAddress, issuerAddress, id), backendSK))
`ts
import { Peer, IOXCAccount } from "@oxc-wallet/protocols";
const issuer = new Peer("0x....");
const remote = await Peer.remote("https://wallet.oxc.sh", "oxford");
const account = await issuer.derive("dev@0xcore.com", remote) as IOXCAccount;
console.log(account.address); // 0x...
`
Encrypt and decrypt data using the derived account's encryption keys:
`ts
// Encrypt a message
const encrypted = await account.encrypt("secret message");
console.log(encrypted); // 0x...
// Decrypt the message
const decrypted = await account.decrypt(encrypted);
console.log(decrypted); // "secret message"
`
Sign messages using the derived account:
`ts
// Sign a string message (EIP-191 personal sign)
const signature = await account.signMessage("Hello World");
// Sign raw bytes (hex-encoded)
const rawSignature = await account.signRawMessage("0xdeadbeef");
`
Signatures can be verified using viem:
`ts
import { verifyMessage } from "viem";
const isValid = await verifyMessage({
address: account.address,
message: "Hello World",
signature,
});
`
Sign transactions using the derived account:
`ts`
const signedTx = await account.signTransaction({
to: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
value: 1000000000000000000n, // 1 ETH
chainId: 1,
nonce: 0,
gas: 21000n,
maxFeePerGas: 20000000000n,
maxPriorityFeePerGas: 1000000000n,
});
All account methods are also available directly on the Peer class:
`ts
const issuer = new Peer("0x....");
const remote = await Peer.remote("https://wallet.oxc.sh", "oxford");
const identifier = "dev@0xcore.com";
// Encrypt/decrypt
const encrypted = await issuer.encrypt(identifier, "message", remote);
const decrypted = await issuer.decrypt(identifier, encrypted, remote);
// Sign message
const signature = await issuer.signMessage(identifier, "Hello", remote);
// Sign raw message
const rawSig = await issuer.signRawMessage(identifier, "0xdeadbeef", remote);
// Sign transaction
const signedTx = await issuer.signTransaction(identifier, transaction, remote);
`
#### Constructor
`ts`
new Peer(secret: Hex)
Creates a new Peer instance with the given private key.
#### Properties
- details - Returns { peer: Hex, address: Hex, pubkey: Hex }
#### Static Methods
- Peer.remote(url: string, protocol: "oxford" | "loophole") - Creates a remote peer stub
#### Instance Methods
| Method | Description | Protocol Support |
|--------|-------------|------------------|
| derive(identifier, stub) | Derive an account for the identifier | oxford, loophole |encrypt(identifier, message, stub)
| | Encrypt a message | oxford |decrypt(identifier, ciphertext, stub)
| | Decrypt a message | oxford |signMessage(identifier, message, stub)
| | Sign a string message | oxford |signRawMessage(identifier, hex, stub)
| | Sign raw hex data | oxford |signTransaction(identifier, tx, stub)
| | Sign a transaction | oxford |handle(parcel, op)
| | Handle incoming requests (server-side) | oxford, loophole |
The account returned by derive() with the oxford protocol:
| Method | Description |
|--------|-------------|
| encrypt(message: string) | Encrypt a message |decrypt(ciphertext: Hex)
| | Decrypt a message |signMessage(message: string)
| | Sign a string message |signRawMessage(message: Hex)
| | Sign raw hex data |signTransaction(tx: TransactionSerializable)
| | Sign a transaction |
- Parcels are x25519-xsalsa20-poly1305 public key encrypted boxes.secp256k1` signature.
- Sender is verified by checking
- Parcels get stale after 10 seconds (timestamp included in payload).
Unlicensed