@realmint/sdk 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -98,6 +98,33 @@ await fundWithX402(
98
98
  // USDC is bridged to the user's Injective smart account, ready for buy().
99
99
  ```
100
100
 
101
+ ## Keyless agent auth
102
+
103
+ An autonomous agent holding only its own EVM key can authenticate without an API
104
+ key: `agentLogin` exchanges an EOA signature for a bearer and returns a ready
105
+ client.
106
+
107
+ ```ts
108
+ import { agentLogin, fundWithX402, signerFromViemAccount } from "@realmint/sdk";
109
+ import { privateKeyToAccount } from "viem/accounts";
110
+
111
+ const account = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY!);
112
+ const { client } = await agentLogin({
113
+ ownerEoa: account.address,
114
+ signMessage: (m) => account.signMessage({ message: m }),
115
+ });
116
+
117
+ await fundWithX402(client, { owner_eoa: account.address, amount_usdc: 5, asset_id: "tslax" }, payX402);
118
+ await client.buy(
119
+ { asset_id: "tslax", source_token: "USDC", amount_in: 5, agent_wallet: account.address },
120
+ signerFromViemAccount(account),
121
+ );
122
+ ```
123
+
124
+ The bearer only authorizes quoting/creating intents for the EOA's own smart
125
+ accounts; moving funds still requires the EOA's UserOp signature. See the
126
+ [reference agent](https://github.com/realmint-io/realmint-x402-agent).
127
+
101
128
  ## Signing model
102
129
 
103
130
  `buy()` signs an **ERC-4337 UserOp v0.7** digest as an EIP-191 `personal_sign`
@@ -0,0 +1,37 @@
1
+ import { RealmintClient } from "./client.js";
2
+ /**
3
+ * Signs an arbitrary text message as an EIP-191 `personal_sign`, returning the
4
+ * 0x signature. Build one from a viem account:
5
+ * (m) => account.signMessage({ message: m })
6
+ * or an EIP-1193 provider:
7
+ * (m) => provider.request({ method: "personal_sign", params: [m, address] })
8
+ */
9
+ export type SignMessage = (message: string) => Promise<string>;
10
+ export interface AgentLoginOptions {
11
+ /** The agent's EVM EOA — the identity it authenticates as. */
12
+ ownerEoa: string;
13
+ /** EIP-191 personal_sign over the challenge text. */
14
+ signMessage: SignMessage;
15
+ baseUrl?: string;
16
+ fetch?: typeof fetch;
17
+ }
18
+ export interface AgentSession {
19
+ /** A `RealmintClient` pre-configured with the minted bearer. */
20
+ client: RealmintClient;
21
+ accessToken: string;
22
+ ownerEoa: string;
23
+ /** Seconds until the bearer expires. */
24
+ expiresIn: number;
25
+ }
26
+ /**
27
+ * Keyless agent login: exchange an EOA signature for a bearer token — no API
28
+ * key. Requests a challenge, signs it, and returns a ready `RealmintClient`.
29
+ *
30
+ * const { client } = await agentLogin({
31
+ * ownerEoa: account.address,
32
+ * signMessage: (m) => account.signMessage({ message: m }),
33
+ * });
34
+ * await client.buy({ asset_id: "tslax", source_token: "USDC", amount_in: 5,
35
+ * agent_wallet: account.address }, sign);
36
+ */
37
+ export declare function agentLogin(opts: AgentLoginOptions): Promise<AgentSession>;
package/dist/agent.js ADDED
@@ -0,0 +1,64 @@
1
+ import { RealmintClient } from "./client.js";
2
+ const DEFAULT_BASE_URL = "https://api.realmint.io";
3
+ /**
4
+ * Keyless agent login: exchange an EOA signature for a bearer token — no API
5
+ * key. Requests a challenge, signs it, and returns a ready `RealmintClient`.
6
+ *
7
+ * const { client } = await agentLogin({
8
+ * ownerEoa: account.address,
9
+ * signMessage: (m) => account.signMessage({ message: m }),
10
+ * });
11
+ * await client.buy({ asset_id: "tslax", source_token: "USDC", amount_in: 5,
12
+ * agent_wallet: account.address }, sign);
13
+ */
14
+ export async function agentLogin(opts) {
15
+ const base = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
16
+ const f = opts.fetch ?? globalThis.fetch;
17
+ if (!f)
18
+ throw new Error("No fetch available — pass `fetch` in options (Node < 18).");
19
+ const challenge = await postJson(f, `${base}/v1/agent/auth/challenge`, {
20
+ owner_eoa: opts.ownerEoa,
21
+ });
22
+ const message = challenge.message;
23
+ const challengeToken = challenge.challenge_token;
24
+ if (!message || !challengeToken) {
25
+ throw new Error(`Unexpected challenge response: ${JSON.stringify(challenge)}`);
26
+ }
27
+ const signature = await opts.signMessage(message);
28
+ const token = await postJson(f, `${base}/v1/agent/auth/token`, {
29
+ challenge_token: challengeToken,
30
+ signature,
31
+ });
32
+ const accessToken = token.access_token;
33
+ if (!accessToken) {
34
+ throw new Error(`Agent auth failed: ${JSON.stringify(token)}`);
35
+ }
36
+ return {
37
+ client: new RealmintClient({ baseUrl: base, bearer: accessToken, fetch: opts.fetch }),
38
+ accessToken,
39
+ ownerEoa: opts.ownerEoa,
40
+ expiresIn: Number(token.expires_in ?? 3600),
41
+ };
42
+ }
43
+ async function postJson(f, url, body) {
44
+ const res = await f(url, {
45
+ method: "POST",
46
+ headers: { "content-type": "application/json" },
47
+ body: JSON.stringify(body),
48
+ });
49
+ const text = await res.text();
50
+ let parsed;
51
+ try {
52
+ parsed = text ? JSON.parse(text) : undefined;
53
+ }
54
+ catch {
55
+ parsed = text;
56
+ }
57
+ if (!res.ok) {
58
+ const msg = parsed?.message ??
59
+ parsed?.error ??
60
+ `HTTP ${res.status}`;
61
+ throw new Error(`${url} → ${msg}`);
62
+ }
63
+ return (parsed ?? {});
64
+ }
package/dist/client.d.ts CHANGED
@@ -15,7 +15,7 @@ export interface WaitOptions {
15
15
  */
16
16
  export declare class RealmintClient {
17
17
  private readonly baseUrl;
18
- private readonly apiKey;
18
+ private readonly authHeaders;
19
19
  private readonly fetchImpl;
20
20
  constructor(opts: RealmintClientOptions);
21
21
  private request;
package/dist/client.js CHANGED
@@ -17,11 +17,20 @@ export class RealmintError extends Error {
17
17
  */
18
18
  export class RealmintClient {
19
19
  baseUrl;
20
- apiKey;
20
+ authHeaders;
21
21
  fetchImpl;
22
22
  constructor(opts) {
23
23
  this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
24
- this.apiKey = opts.apiKey;
24
+ // Bearer (keyless agent auth) takes precedence; fall back to an API key.
25
+ if (opts.bearer) {
26
+ this.authHeaders = { authorization: `Bearer ${opts.bearer}` };
27
+ }
28
+ else if (opts.apiKey) {
29
+ this.authHeaders = { "x-api-key": opts.apiKey };
30
+ }
31
+ else {
32
+ throw new Error("Provide either `apiKey` or `bearer` in RealmintClient options.");
33
+ }
25
34
  this.fetchImpl = opts.fetch ?? globalThis.fetch;
26
35
  if (!this.fetchImpl) {
27
36
  throw new Error("No fetch available — pass `fetch` in options (Node < 18).");
@@ -32,7 +41,7 @@ export class RealmintClient {
32
41
  const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
33
42
  method,
34
43
  headers: {
35
- "x-api-key": this.apiKey,
44
+ ...this.authHeaders,
36
45
  ...(body !== undefined ? { "content-type": "application/json" } : {}),
37
46
  },
38
47
  body: body !== undefined ? JSON.stringify(body) : undefined,
package/dist/index.d.ts CHANGED
@@ -3,5 +3,7 @@ export type { WaitOptions } from "./client.js";
3
3
  export { signerFromViemAccount, signerFromEip1193 } from "./signing.js";
4
4
  export { fundWithX402 } from "./x402.js";
5
5
  export type { X402PaymentSigner } from "./x402.js";
6
+ export { agentLogin } from "./agent.js";
7
+ export type { AgentLoginOptions, AgentSession, SignMessage } from "./agent.js";
6
8
  export type { AccountStatus, CreateIntentParams, CreateIntentResponse, ExecuteResponse, GetIntentResponse, Hex, PrepareResponse, PrepareSolanaSellResponse, QuoteBreakdown, RealmintClientOptions, RouteIntent, SignSolanaWire, SignUserOpHash, TradeAction, UnsignedUserOp, } from "./types.js";
7
9
  export { TERMINAL_STATES } from "./types.js";
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { RealmintClient, RealmintError } from "./client.js";
2
2
  export { signerFromViemAccount, signerFromEip1193 } from "./signing.js";
3
3
  export { fundWithX402 } from "./x402.js";
4
+ export { agentLogin } from "./agent.js";
4
5
  export { TERMINAL_STATES } from "./types.js";
package/dist/types.d.ts CHANGED
@@ -2,8 +2,14 @@ export type Hex = `0x${string}`;
2
2
  export interface RealmintClientOptions {
3
3
  /** API base URL. Defaults to https://api.realmint.io */
4
4
  baseUrl?: string;
5
- /** Partner API key, sent as `x-api-key`. */
6
- apiKey: string;
5
+ /** Partner API key, sent as `x-api-key`. Provide this or `bearer`. */
6
+ apiKey?: string;
7
+ /**
8
+ * OAuth bearer token, sent as `Authorization: Bearer`. Mint one keyless from
9
+ * an EOA signature with `agentLogin` — no API key needed. Takes precedence
10
+ * over `apiKey` when both are set.
11
+ */
12
+ bearer?: string;
7
13
  /** Override the global fetch (e.g. a proxy-aware fetch). */
8
14
  fetch?: typeof fetch;
9
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@realmint/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "TypeScript SDK for the Realmint API — list, buy, and sell tokenized RWAs (non-custodial; the partner's signer signs).",
5
5
  "license": "MIT",
6
6
  "type": "module",