@edge-protocol/sdk 0.5.1 → 0.5.2

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.
Files changed (3) hide show
  1. package/DOCS.md +112 -152
  2. package/README.md +18 -0
  3. package/package.json +1 -1
package/DOCS.md CHANGED
@@ -22,6 +22,10 @@ pnpm add @edge-protocol/sdk
22
22
  - [Error Handling](#error-handling)
23
23
  - [Architecture](#architecture)
24
24
  - [Move Contract](#move-contract)
25
+ - [Competitive Positioning](#competitive-positioning)
26
+ - [Security Model](#security-model)
27
+ - [Testing](#testing)
28
+ - [Links](#links)
25
29
 
26
30
  ---
27
31
 
@@ -178,17 +182,11 @@ const outcome = await sdk.execute(pass, {
178
182
  switch (outcome.status) {
179
183
  case 'approved':
180
184
  console.log('tx digest:', outcome.digest);
181
- // audit receipt written to Walrus automatically
182
185
  break;
183
-
184
186
  case 'escalated':
185
- // notify user — outcome.reason explains why
186
187
  await sendPushNotification(outcome.reason);
187
- // re-execute after user approves
188
188
  break;
189
-
190
189
  case 'blocked':
191
- // policy rejected — outcome.reason explains why
192
190
  console.log('blocked:', outcome.reason);
193
191
  break;
194
192
  }
@@ -198,7 +196,7 @@ switch (outcome.status) {
198
196
 
199
197
  ### `sdk.validate(pass, request)` → `PolicyValidation`
200
198
 
201
- Preview the outcome without executing. Zero network calls. Sub-millisecond. Use for UI previews before execution.
199
+ Preview the outcome without executing. Zero network calls. Sub-millisecond.
202
200
 
203
201
  ```typescript
204
202
  const preview = sdk.validate(pass, {
@@ -206,10 +204,6 @@ const preview = sdk.validate(pass, {
206
204
  amount: 18_500_000_000n,
207
205
  });
208
206
 
209
- // { allowed: true, requiresEscalation: false, reason: 'Auto-approved' }
210
- // { allowed: true, requiresEscalation: true, reason: 'Amount exceeds escalation threshold...' }
211
- // { allowed: false, requiresEscalation: false, reason: 'Merchant "X" is not approved' }
212
-
213
207
  if (!preview.allowed) {
214
208
  showBlockedUI(preview.reason);
215
209
  return;
@@ -220,7 +214,6 @@ if (preview.requiresEscalation) {
220
214
  return;
221
215
  }
222
216
 
223
- // Safe to execute
224
217
  const outcome = await sdk.execute(pass, request, signer);
225
218
  ```
226
219
 
@@ -243,14 +236,8 @@ Fetch a live EdgePass from the Sui network.
243
236
 
244
237
  ```typescript
245
238
  const pass = await sdk.fetch('0x4e2f...8b91');
246
-
247
- if (!pass) {
248
- console.log('EdgePass not found');
249
- return;
250
- }
251
-
239
+ if (!pass) { console.log('EdgePass not found'); return; }
252
240
  const remaining = sdk.remainingBudget(pass);
253
- console.log('remaining:', remaining);
254
241
  ```
255
242
 
256
243
  ---
@@ -273,7 +260,6 @@ Returns `true` if the pass is active and not expired.
273
260
 
274
261
  ```typescript
275
262
  if (!sdk.isValid(pass)) {
276
- // create a new pass
277
263
  pass = await sdk.create(config, signer);
278
264
  }
279
265
  ```
@@ -297,21 +283,15 @@ sdk
297
283
  logger.warn(`blocked: ${outcome.reason}`);
298
284
  });
299
285
 
300
- // events fire automatically on execute
301
286
  await sdk.execute(pass, request, signer);
302
287
  ```
303
288
 
304
289
  **Event payload:**
305
290
 
306
291
  ```typescript
307
- // approved
308
- { type: 'approved', outcome: { status: 'approved', digest: string, auto: true }, pass, request }
309
-
310
- // escalated
292
+ { type: 'approved', outcome: { status: 'approved', digest: string, auto: true }, pass, request }
311
293
  { type: 'escalated', outcome: { status: 'escalated', reason: string, auto: false }, pass, request }
312
-
313
- // blocked
314
- { type: 'blocked', outcome: { status: 'blocked', reason: string, auto: false }, pass, request }
294
+ { type: 'blocked', outcome: { status: 'blocked', reason: string, auto: false }, pass, request }
315
295
  ```
316
296
 
317
297
  ---
@@ -322,9 +302,7 @@ Remove a specific listener.
322
302
 
323
303
  ```typescript
324
304
  const onApproved = ({ outcome }) => console.log(outcome.digest);
325
-
326
305
  sdk.on('approved', onApproved);
327
- // later...
328
306
  sdk.off('approved', onApproved);
329
307
  ```
330
308
 
@@ -348,7 +326,6 @@ Pre-configured trust boundaries for common use cases. Every template is a starti
348
326
  ```typescript
349
327
  import { EdgePass, EDGE_TEMPLATES } from '@edge-protocol/sdk';
350
328
 
351
- // Available templates
352
329
  EdgePass.fromTemplate('festival', { owner }) // $300 / 48h
353
330
  EdgePass.fromTemplate('gaming', { owner }) // $50 / 4h session
354
331
  EdgePass.fromTemplate('subscription', { owner }) // $200 / 30 days
@@ -366,15 +343,6 @@ EdgePass.fromTemplate('enterprise', { owner }) // $50k / 30 days
366
343
  | `defi` | 10,000 SUI | 500 SUI | 1,000 SUI | 2,000 SUI | 7d |
367
344
  | `enterprise` | 50,000 SUI | 1,000 SUI | 5,000 SUI | 10,000 SUI | 30d |
368
345
 
369
- ### Accessing raw template values
370
-
371
- ```typescript
372
- import { EDGE_TEMPLATES } from '@edge-protocol/sdk';
373
-
374
- console.log(EDGE_TEMPLATES.festival.budget); // 300_000_000_000n
375
- console.log(EDGE_TEMPLATES.defi.escalateThreshold); // 1_000_000_000_000n
376
- ```
377
-
378
346
  ---
379
347
 
380
348
  ## PolicyEngine
@@ -387,17 +355,12 @@ import { PolicyEngine } from '@edge-protocol/sdk';
387
355
 
388
356
  ### `PolicyEngine.validate(pass, request)` → `PolicyValidation`
389
357
 
390
- Validates a transaction request. Same logic used internally by `sdk.execute()`.
391
-
392
358
  ```typescript
393
359
  const validation = PolicyEngine.validate(pass, {
394
360
  merchant: 'Shuttle Express',
395
361
  amount: 18_500_000_000n,
396
362
  });
397
-
398
- // validation.allowed → boolean
399
- // validation.requiresEscalation → boolean
400
- // validation.reason → string
363
+ // validation.allowed · validation.requiresEscalation · validation.reason
401
364
  ```
402
365
 
403
366
  **Validation rules (in order):**
@@ -412,16 +375,8 @@ const validation = PolicyEngine.validate(pass, {
412
375
 
413
376
  ### `PolicyEngine.isValid(pass)` → `boolean`
414
377
 
415
- ```typescript
416
- const valid = PolicyEngine.isValid(pass);
417
- ```
418
-
419
378
  ### `PolicyEngine.remainingBudget(pass)` → `bigint`
420
379
 
421
- ```typescript
422
- const remaining = PolicyEngine.remainingBudget(pass);
423
- ```
424
-
425
380
  ---
426
381
 
427
382
  ## Types
@@ -443,13 +398,13 @@ import type {
443
398
 
444
399
  ```typescript
445
400
  interface EdgePassConfig {
446
- budget: bigint; // total spend limit in MIST
447
- autoThreshold: bigint; // auto-approve below this
448
- escalateThreshold: bigint; // escalate above this
449
- maxPerTransaction?: bigint; // optional hard cap per tx
450
- approvedMerchants: string[]; // merchant allowlist
451
- expiryMs: number; // duration in milliseconds
452
- owner: string; // Sui address
401
+ budget: bigint;
402
+ autoThreshold: bigint;
403
+ escalateThreshold: bigint;
404
+ maxPerTransaction?: bigint;
405
+ approvedMerchants: string[];
406
+ expiryMs: number;
407
+ owner: string;
453
408
  }
454
409
  ```
455
410
 
@@ -457,12 +412,12 @@ interface EdgePassConfig {
457
412
 
458
413
  ```typescript
459
414
  interface EdgePassObject {
460
- id: string; // Sui object ID
415
+ id: string;
461
416
  config: EdgePassConfig;
462
- spent: bigint; // total spent so far in MIST
417
+ spent: bigint;
463
418
  active: boolean;
464
- createdAt: number; // Unix timestamp
465
- expiresAt: number; // Unix timestamp
419
+ createdAt: number;
420
+ expiresAt: number;
466
421
  }
467
422
  ```
468
423
 
@@ -470,9 +425,9 @@ interface EdgePassObject {
470
425
 
471
426
  ```typescript
472
427
  interface TransactionRequest {
473
- merchant: string; // merchant identifier
474
- amount: bigint; // amount in MIST
475
- metadata?: Record<string, string>; // optional metadata
428
+ merchant: string;
429
+ amount: bigint;
430
+ metadata?: Record<string, string>;
476
431
  }
477
432
  ```
478
433
 
@@ -489,9 +444,9 @@ type TransactionOutcome =
489
444
 
490
445
  ```typescript
491
446
  interface PolicyValidation {
492
- allowed: boolean;
493
- requiresEscalation: boolean;
494
- reason: string;
447
+ allowed: boolean;
448
+ requiresEscalation: boolean;
449
+ reason: string;
495
450
  }
496
451
  ```
497
452
 
@@ -527,8 +482,6 @@ import {
527
482
 
528
483
  ### AI Agent with EdgePass
529
484
 
530
- A Claude LLM making autonomous festival purchases within an EdgePass.
531
-
532
485
  ```typescript
533
486
  import { EdgePass, MIST_PER_SUI } from '@edge-protocol/sdk';
534
487
  import Anthropic from '@anthropic-ai/sdk';
@@ -536,7 +489,6 @@ import Anthropic from '@anthropic-ai/sdk';
536
489
  const sdk = new EdgePass({ network: 'mainnet', enokiApiKey: KEY });
537
490
  const claude = new Anthropic();
538
491
 
539
- // 1. User creates EdgePass once
540
492
  const pass = await sdk.create(
541
493
  EdgePass.fromTemplate('festival', {
542
494
  approvedMerchants: ['Shuttle Express', 'Hydra Bar', 'Stage Access VIP'],
@@ -545,30 +497,20 @@ const pass = await sdk.create(
545
497
  signer
546
498
  );
547
499
 
548
- // 2. Agent loop — runs autonomously
549
500
  async function agentLoop(scenario: string) {
550
501
  const response = await claude.messages.create({
551
502
  model: 'claude-sonnet-4-6',
552
503
  max_tokens: 500,
553
- messages: [{
554
- role: 'user',
555
- content: `Festival scenario: "${scenario}".
556
- Decide what to purchase. Return JSON:
557
- { merchant: string, amount: number }`
558
- }]
504
+ messages: [{ role: 'user', content: `Festival scenario: "${scenario}". Return JSON: { merchant: string, amount: number }` }]
559
505
  });
560
506
 
561
507
  const { merchant, amount } = JSON.parse(response.content[0].text);
562
-
563
508
  const outcome = await sdk.execute(pass, {
564
509
  merchant,
565
510
  amount: BigInt(Math.floor(amount * 1e9)),
566
511
  }, signer);
567
512
 
568
- if (outcome.status === 'escalated') {
569
- await notifyUser(`Approve $${amount} at ${merchant}?`);
570
- }
571
-
513
+ if (outcome.status === 'escalated') await notifyUser(`Approve $${amount} at ${merchant}?`);
572
514
  return outcome;
573
515
  }
574
516
  ```
@@ -588,21 +530,10 @@ const pass = await sdk.create(
588
530
  );
589
531
 
590
532
  async function executeTrade(dex: string, amount: bigint) {
591
- // validate before network call
592
533
  const preview = sdk.validate(pass, { merchant: dex, amount });
593
-
594
- if (!preview.allowed) {
595
- logger.warn(`Trade blocked: ${preview.reason}`);
596
- return;
597
- }
598
-
599
- if (preview.requiresEscalation) {
600
- await riskTeam.requestApproval({ dex, amount, reason: preview.reason });
601
- return;
602
- }
603
-
534
+ if (!preview.allowed) { logger.warn(`blocked: ${preview.reason}`); return; }
535
+ if (preview.requiresEscalation) { await riskTeam.requestApproval({ dex, amount }); return; }
604
536
  const outcome = await sdk.execute(pass, { merchant: dex, amount }, signer);
605
- // audit log written to Walrus automatically
606
537
  logger.info(`Trade executed: ${outcome.digest}`);
607
538
  }
608
539
  ```
@@ -616,22 +547,15 @@ const pass = await sdk.create(
616
547
  EdgePass.fromTemplate('enterprise', {
617
548
  approvedMerchants: ['vendor-a.sui', 'vendor-b.sui'],
618
549
  budget: 100_000n * MIST_PER_SUI,
619
- escalateThreshold: 10_000n * MIST_PER_SUI,
550
+ escalateThreshold: 10_000n * MIST_PER_SUI,
620
551
  owner: cfoAddress,
621
552
  }),
622
553
  signer
623
554
  );
624
555
 
625
556
  for (const payment of scheduledPayments) {
626
- const outcome = await sdk.execute(pass, {
627
- merchant: payment.vendor,
628
- amount: payment.amount,
629
- }, signer);
630
-
631
- if (outcome.status === 'escalated') {
632
- await cfo.requestApproval(payment); // new vendor or large amount
633
- }
634
- // every payment logged to Walrus for compliance audit
557
+ const outcome = await sdk.execute(pass, { merchant: payment.vendor, amount: payment.amount }, signer);
558
+ if (outcome.status === 'escalated') await cfo.requestApproval(payment);
635
559
  }
636
560
  ```
637
561
 
@@ -648,20 +572,12 @@ const pass = await sdk.create(
648
572
  signer
649
573
  );
650
574
 
651
- // Runs monthly — agent handles all renewals
652
575
  async function processRenewals(subscriptions: Subscription[]) {
653
576
  for (const sub of subscriptions) {
654
577
  if (!sdk.isValid(pass)) {
655
- pass = await sdk.create(
656
- EdgePass.fromTemplate('subscription', { owner: userAddress }),
657
- signer
658
- );
578
+ pass = await sdk.create(EdgePass.fromTemplate('subscription', { owner: userAddress }), signer);
659
579
  }
660
-
661
- await sdk.execute(pass, {
662
- merchant: sub.merchant,
663
- amount: sub.amount,
664
- }, signer);
580
+ await sdk.execute(pass, { merchant: sub.merchant, amount: sub.amount }, signer);
665
581
  }
666
582
  }
667
583
  ```
@@ -673,20 +589,12 @@ async function processRenewals(subscriptions: Subscription[]) {
673
589
  ```typescript
674
590
  try {
675
591
  const outcome = await sdk.execute(pass, request, signer);
676
-
677
592
  switch (outcome.status) {
678
- case 'approved':
679
- // success outcome.digest is the Sui tx hash
680
- break;
681
- case 'escalated':
682
- // needs human review — outcome.reason explains why
683
- break;
684
- case 'blocked':
685
- // policy rejected — outcome.reason explains why
686
- break;
593
+ case 'approved': break; // outcome.digest is the Sui tx hash
594
+ case 'escalated': break; // outcome.reason explains why
595
+ case 'blocked': break; // outcome.reason explains why
687
596
  }
688
597
  } catch (error) {
689
- // network error, signing failure, expired credentials, etc.
690
598
  console.error('SDK error:', error);
691
599
  }
692
600
  ```
@@ -700,7 +608,7 @@ try {
700
608
  | `Merchant "X" is not approved` | Merchant not in allowlist |
701
609
  | `Insufficient budget` | Remaining budget < amount |
702
610
  | `Amount exceeds per-transaction limit` | Amount > `maxPerTransaction` |
703
- | `Amount exceeds escalation threshold` | Amount > `escalateThreshold` (not blocked — escalated) |
611
+ | `Amount exceeds escalation threshold` | Amount > `escalateThreshold` — escalated not blocked |
704
612
 
705
613
  ---
706
614
 
@@ -728,11 +636,11 @@ Agent calls sdk.execute() — many times, autonomously
728
636
 
729
637
  ### Why PTBs matter
730
638
 
731
- PTBs (Programmable Transaction Blocks) are Sui's killer feature. The policy check and the spend update happen in one atomic block. If any step fails, everything reverts. No partial state. No race conditions. This is what makes EdgePass trustworthy at the protocol level — not just application code.
639
+ PTBs are Sui's killer feature. The policy check and the spend update happen in one atomic block. If any step fails, everything reverts. No partial state. No race conditions.
732
640
 
733
641
  ### Why the object model matters
734
642
 
735
- The EdgePass is a first-class owned object in the user's wallet. An agent can be passed the object to execute against — but the Sui protocol guarantees it can never take ownership. No contract upgrade, no admin key, no reentrancy attack can change that.
643
+ The EdgePass is a first-class owned object in the user's wallet. An agent executes against it without ever taking ownership. No contract upgrade, no admin key, no reentrancy attack can change that.
736
644
 
737
645
  ---
738
646
 
@@ -748,35 +656,87 @@ Network: Sui Testnet (Mainnet coming)
748
656
  ### Contract functions
749
657
 
750
658
  ```move
751
- // Create a new EdgePass
752
659
  public entry fun create_pass(
753
- budget: u64,
754
- auto_threshold: u64,
755
- escalate_threshold: u64,
756
- expiry_ms: u64,
757
- approved_merchants: vector<String>,
758
- clock: &Clock,
759
- ctx: &mut TxContext,
660
+ budget: u64, auto_threshold: u64, escalate_threshold: u64,
661
+ expiry_ms: u64, approved_merchants: vector<String>,
662
+ clock: &Clock, ctx: &mut TxContext,
760
663
  )
761
664
 
762
- // Execute a transaction against an EdgePass
763
665
  public entry fun execute_transaction(
764
- pass: &mut EdgePass,
765
- amount: u64,
766
- merchant: String,
767
- clock: &Clock,
768
- ctx: &mut TxContext,
666
+ pass: &mut EdgePass, amount: u64, merchant: String,
667
+ clock: &Clock, ctx: &mut TxContext,
769
668
  )
770
669
 
771
- // Revoke an EdgePass
772
- public entry fun revoke_pass(
773
- pass: &mut EdgePass,
774
- ctx: &mut TxContext,
775
- )
670
+ public entry fun revoke_pass(pass: &mut EdgePass, ctx: &mut TxContext)
776
671
  ```
777
672
 
778
673
  ---
779
674
 
675
+ ## Competitive Positioning
676
+
677
+ Edge is the **policy layer** for the agentic economy. It is not a payment rail.
678
+
679
+ | Solution | Layer | Open Source | Sui Native | 3-line SDK |
680
+ |----------|-------|-------------|------------|------------|
681
+ | **Edge Protocol** | Policy enforcement | ✅ | ✅ | ✅ |
682
+ | x402 (Coinbase) | Payment rail | ✅ | ❌ | ❌ |
683
+ | ERC-4337 | Account abstraction | ✅ | ❌ EVM only | ❌ |
684
+ | Trust Wallet Agent Kit | Wallet interactions | ✅ | Partial | ❌ |
685
+ | Cobo Agentic Wallet | Custody | ❌ Enterprise | ❌ | ❌ |
686
+ | Nevermined | Metering + monetization | Partial | ❌ | ❌ |
687
+ | Skyfire | Identity + settlement | ❌ | ❌ | ❌ |
688
+
689
+ **Edge complements x402, it does not compete with it.**
690
+
691
+ x402 answers: *how does money move from agent to merchant?*
692
+ Edge answers: *should this agent be allowed to spend this money at all?*
693
+
694
+ Together they form a complete stack:
695
+
696
+ ```
697
+ Edge (policy layer) → x402 (payment rail) → Settlement
698
+ "is this allowed?" "move the money"
699
+ ```
700
+
701
+ ---
702
+
703
+ ## Security Model
704
+
705
+ Edge has two enforcement layers:
706
+
707
+ ### Layer 1 — TypeScript PolicyEngine (pre-flight)
708
+
709
+ Fast, zero network calls, under 1ms. Can be bypassed by a compromised agent runtime. Treat as a UX convenience and performance optimization — not a security boundary.
710
+
711
+ ### Layer 2 — Sui Move Contract (source of truth)
712
+
713
+ On-chain enforcement by the Sui VM. Cannot be bypassed. The EdgePass object validates budget, expiry, and merchant allowlist independently at the protocol level.
714
+
715
+ **For production deployments:** Always execute via the Move contract. The TypeScript layer is a preview — the chain is the guarantee.
716
+
717
+ ### The Two-Layer Pattern
718
+
719
+ ```
720
+ sdk.validate() → TypeScript (instant preview, saves gas on rejections)
721
+ sdk.execute() → TypeScript + Move contract (atomic, tamper-proof, final)
722
+ ```
723
+
724
+ ### Production Guidelines
725
+
726
+ - Fetch EdgePass state from chain before executing — never trust locally cached config
727
+ - Use on-chain clock (Sui Clock `0x6`) for expiry verification in high-security deployments
728
+ - Use verified Sui addresses in `approvedMerchants` rather than display name strings
729
+ - Keep ephemeral zkLogin keys in memory only — never persist to localStorage
730
+
731
+ ### Known V2 Security Improvements
732
+
733
+ - Rolling time windows — `maxTransactionsPerHour`
734
+ - On-chain policy signatures — cryptographic commitment prevents client-side tampering
735
+ - Merchant address verification — verified Sui addresses on-chain
736
+ - Rate limiting — prevent rapid budget drain attacks
737
+
738
+ ---
739
+
780
740
  ## Testing
781
741
 
782
742
  ```bash
package/README.md CHANGED
@@ -233,6 +233,24 @@ pnpm test
233
233
  34/34 passing ✅
234
234
  ```
235
235
 
236
+
237
+ ---
238
+
239
+ ## Security Model
240
+
241
+ Edge has two enforcement layers:
242
+
243
+ **Layer 1 — TypeScript PolicyEngine** — pre-flight validation, zero network calls, under 1ms. Can be bypassed by a malicious agent runtime. Use as a UX convenience, not a security boundary.
244
+
245
+ **Layer 2 — Sui Move Contract** — on-chain enforcement by the Sui VM. Cannot be bypassed. The EdgePass object validates budget, expiry, and merchant allowlist independently. This is the source of truth.
246
+
247
+ ```
248
+ sdk.validate() → TypeScript check (fast preview, no gas)
249
+ sdk.execute() → TypeScript check + Move contract check (atomic, final)
250
+ ```
251
+
252
+ For production: always execute via the Move contract. The TypeScript layer is a preview — the chain is the guarantee.
253
+
236
254
  ---
237
255
 
238
256
  ## Move Contract
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edge-protocol/sdk",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Programmable trust infrastructure for autonomous AI agents on Sui. Give agents your rules, not your keys.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",