@mida-context/sdk 0.1.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.
Files changed (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +26 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.js +7115 -0
  5. package/dist/types/api/app.d.ts +99 -0
  6. package/dist/types/api/auth-pure.d.ts +51 -0
  7. package/dist/types/api/auth.d.ts +17 -0
  8. package/dist/types/api/authorize.d.ts +24 -0
  9. package/dist/types/api/batch-deny.d.ts +22 -0
  10. package/dist/types/api/batch-routes.d.ts +69 -0
  11. package/dist/types/api/batch-store.d.ts +96 -0
  12. package/dist/types/api/batcher.d.ts +220 -0
  13. package/dist/types/api/browser.d.ts +14 -0
  14. package/dist/types/api/chain-budget.d.ts +61 -0
  15. package/dist/types/api/chain-views.d.ts +87 -0
  16. package/dist/types/api/client.d.ts +295 -0
  17. package/dist/types/api/deny-overlay.d.ts +81 -0
  18. package/dist/types/api/errors.d.ts +30 -0
  19. package/dist/types/api/file-stores.d.ts +7 -0
  20. package/dist/types/api/index.d.ts +17 -0
  21. package/dist/types/api/secure-fs.d.ts +12 -0
  22. package/dist/types/api/store.d.ts +51 -0
  23. package/dist/types/api/stores.d.ts +171 -0
  24. package/dist/types/api/verify-assertion.d.ts +22 -0
  25. package/dist/types/api/wire.d.ts +27 -0
  26. package/dist/types/chain/abis.d.ts +2125 -0
  27. package/dist/types/chain/browser.d.ts +20 -0
  28. package/dist/types/chain/deployment-fs.d.ts +15 -0
  29. package/dist/types/chain/deployment.d.ts +33 -0
  30. package/dist/types/chain/deployments.generated.d.ts +1 -0
  31. package/dist/types/chain/gas.d.ts +47 -0
  32. package/dist/types/chain/history.d.ts +52 -0
  33. package/dist/types/chain/index.d.ts +13 -0
  34. package/dist/types/chain/local.d.ts +34 -0
  35. package/dist/types/chain/logs.d.ts +62 -0
  36. package/dist/types/chain/placements.d.ts +92 -0
  37. package/dist/types/chain/read-scope.d.ts +66 -0
  38. package/dist/types/chain/registry.d.ts +16 -0
  39. package/dist/types/chain/sponsored.d.ts +93 -0
  40. package/dist/types/chain/transport.d.ts +53 -0
  41. package/dist/types/chain/writes.d.ts +165 -0
  42. package/dist/types/crypto/aead.d.ts +5 -0
  43. package/dist/types/crypto/bytes.d.ts +4 -0
  44. package/dist/types/crypto/derive.d.ts +20 -0
  45. package/dist/types/crypto/index.d.ts +6 -0
  46. package/dist/types/crypto/object.d.ts +42 -0
  47. package/dist/types/crypto/payload.d.ts +21 -0
  48. package/dist/types/crypto/wraps.d.ts +36 -0
  49. package/dist/types/grant-advisor/advise.d.ts +23 -0
  50. package/dist/types/grant-advisor/authority.d.ts +44 -0
  51. package/dist/types/grant-advisor/index.d.ts +5 -0
  52. package/dist/types/grant-advisor/manifest.d.ts +66 -0
  53. package/dist/types/grant-advisor/policy.d.ts +91 -0
  54. package/dist/types/grant-advisor/signatures.d.ts +10 -0
  55. package/dist/types/mida-context-sdk/daemon.d.ts +27 -0
  56. package/dist/types/mida-context-sdk/errors.d.ts +27 -0
  57. package/dist/types/mida-context-sdk/index.d.ts +5 -0
  58. package/dist/types/mida-context-sdk/local.d.ts +40 -0
  59. package/dist/types/mida-context-sdk/mida.d.ts +65 -0
  60. package/dist/types/mida-context-sdk/transport.d.ts +137 -0
  61. package/dist/types/protocol/batch.d.ts +161 -0
  62. package/dist/types/protocol/constants.d.ts +56 -0
  63. package/dist/types/protocol/errors.d.ts +15 -0
  64. package/dist/types/protocol/ids.d.ts +72 -0
  65. package/dist/types/protocol/index.d.ts +11 -0
  66. package/dist/types/protocol/namespaces.d.ts +15 -0
  67. package/dist/types/protocol/owner-link.d.ts +146 -0
  68. package/dist/types/protocol/typed-data.d.ts +365 -0
  69. package/dist/types/protocol/types.d.ts +215 -0
  70. package/dist/types/protocol/webauthn-assertion.d.ts +29 -0
  71. package/dist/types/protocol/wire.d.ts +8 -0
  72. package/dist/types/sdk/agent.d.ts +279 -0
  73. package/dist/types/sdk/batched.d.ts +68 -0
  74. package/dist/types/sdk/connect.d.ts +88 -0
  75. package/dist/types/sdk/index.d.ts +7 -0
  76. package/dist/types/sdk/request-store.d.ts +26 -0
  77. package/dist/types/storage/index.d.ts +23 -0
  78. package/package.json +50 -0
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The browser-safe surface of @mida/chain for the owner page bundle. index.ts also exports
3
+ * local.ts (node:child_process, node:fs, node:net, node:os) and deployment.ts's loadDeployment
4
+ * (node:fs) — neither can exist in a browser bundle, so this entry names the safe modules
5
+ * explicitly. The bundle test in apps/owner-page scans the built output and fails on "node:".
6
+ *
7
+ * deployment.js is re-exported only partially on purpose: parseDeployment/chainFor/Deployment are
8
+ * pure data + viem chains, while loadDeployment and DEFAULT_DEPLOYMENTS_DIR touch the filesystem
9
+ * and are deliberately absent here so nothing can reach them transitively.
10
+ */
11
+ export * from "./abis.js";
12
+ export { LOCAL_CHAIN_ID, MONAD_TESTNET_CHAIN_ID, MULTICALL3_ADDRESS, chainFor, parseDeployment } from "./deployment.js";
13
+ export type { Deployment } from "./deployment.js";
14
+ export * from "./logs.js";
15
+ export * from "./history.js";
16
+ export * from "./registry.js";
17
+ export * from "./gas.js";
18
+ export * from "./writes.js";
19
+ export * from "./sponsored.js";
20
+ export * from "./transport.js";
@@ -0,0 +1,15 @@
1
+ import type { Deployment } from "./deployment.js";
2
+ /**
3
+ * The contracts/deployments directory, resolved on first use and cached. It must stay lazy: this module is
4
+ * bundled into the store Worker, where `import.meta.url` is not a parseable URL — evaluating
5
+ * fileURLToPath(new URL(…)) at module scope would throw on startup. The Worker never calls this.
6
+ */
7
+ export declare function DEFAULT_DEPLOYMENTS_DIR(): string;
8
+ /**
9
+ * The deployment record for a chain. An explicit `directory` always wins — that stays the
10
+ * local-Anvil override (its 31337.json is gitignored and is never embedded). Without one, the
11
+ * committed record compiled into this package answers first, so a bundled binary needs no
12
+ * contracts/deployments folder; a chain with no embedded record still falls back to the
13
+ * source-tree file (the repo's own dev runs).
14
+ */
15
+ export declare function loadDeployment(chainId: bigint, directory?: string): Deployment;
@@ -0,0 +1,33 @@
1
+ import type { Address, Hex } from "../protocol/index.js";
2
+ import type { Chain } from "viem";
3
+ /** Contents of contracts/deployments/<chainId>.json, written by contracts/script/Deploy.s.sol (plan Task 20). */
4
+ export interface Deployment {
5
+ chainId: bigint;
6
+ capabilityRegistry: Address;
7
+ contextRegistry: Address;
8
+ deploymentBlock: bigint;
9
+ policyHashV1: Hex;
10
+ vaultRpId: string;
11
+ vaultRpIdHash: Hex;
12
+ /** Set only after DeployBatchAnchor.s.sol has run beside the registries; both keys or neither. */
13
+ batchAnchor?: Address;
14
+ batchAnchorBlock?: bigint;
15
+ }
16
+ export declare const LOCAL_CHAIN_ID = 31337n;
17
+ export declare const MONAD_TESTNET_CHAIN_ID = 10143n;
18
+ export declare function parseDeployment(json: unknown): Deployment;
19
+ /** Only the two networks Project 1 targets. Monad testnet comes from viem, never a hand-written object. */
20
+ export declare function chainFor(chainId: bigint): Chain;
21
+ /** The canonical Multicall3 deployment — present on Monad testnet (chain 10143) at this address. */
22
+ export declare const MULTICALL3_ADDRESS: Address;
23
+ /**
24
+ * The chain a standalone read client is built on when only the deployment's chain id is known —
25
+ * the hosted store learns its config from env vars, not a checked-in network file. viem engages
26
+ * `batch: { multicall: true }` only when the client's chain declares a Multicall3 contract; a
27
+ * chain-less client silently sends one eth_call per read (in-13 M-2 — the Sep 25 RPC-limit
28
+ * incident's root cause). Monad testnet pins the canonical Multicall3 here so the store and the
29
+ * explicit batch reader agree on one shared constant; the local Anvil chain (foundry) and any
30
+ * unknown chain id declare none — it is not verifiable from here whether such a chain carries
31
+ * the contract, so reads stay one-per-call there exactly as before instead of throwing.
32
+ */
33
+ export declare function rpcChain(chainId: bigint): Chain;
@@ -0,0 +1 @@
1
+ export declare const EMBEDDED_DEPLOYMENTS: Record<string, unknown>;
@@ -0,0 +1,47 @@
1
+ import type { Abi, Address } from "viem";
2
+ import type { WriteContext } from "./writes.js";
3
+ /**
4
+ * Gas ceilings per transaction kind (M2 fix round 3, R3-1).
5
+ *
6
+ * Monad bills a transaction for the gas LIMIT it carries, not the gas it uses, and a reverted
7
+ * transaction still pays its full limit — so the limit attached to every send must be a number
8
+ * we chose. A send whose node estimate exceeds its kind's ceiling is refused, not sent.
9
+ *
10
+ * Measured basis: `bench/gas-measure.ts` on local Anvil, default and "prague" hardforks
11
+ * (prague forces the Solidity P256 fallback, so its numbers dominate for the P256 paths).
12
+ * "tx.gas" is the limit the node would attach; "gasUsed" is what the receipt reported.
13
+ * Those ceilings were measured on a local chain with Ethereum gas pricing — Monad prices cold
14
+ * reads about 4x higher (contract tests rose 8.5% overall, up to 26% for revoke-everywhere), so
15
+ * the Monad testnet figures are the ones to trust.
16
+ */
17
+ export declare const GAS_CEILINGS: {
18
+ readonly funding: 50000n;
19
+ readonly "owner.key": 200000n;
20
+ readonly "owner.keyRotate": 900000n;
21
+ readonly "epoch.init": 150000n;
22
+ readonly "epoch.rotateExpired": 300000n;
23
+ readonly "agent.register": 450000n;
24
+ readonly "context.register": 650000n;
25
+ readonly "grant.batch": 1500000n;
26
+ readonly "revoke.capability": 150000n;
27
+ readonly "revoke.rotate": 450000n;
28
+ readonly "revoke.agent": 6000000n;
29
+ readonly "batch.submit": 28000000n;
30
+ };
31
+ /** The named kind every transaction send must declare; a send with no kind does not compile. */
32
+ export type TxKind = keyof typeof GAS_CEILINGS;
33
+ /**
34
+ * The gas limit for a contract write: the node's estimate, sent explicitly and unpadded, refused
35
+ * when it exceeds the kind's ceiling. An estimate call that fails surfaces its own error.
36
+ */
37
+ export declare function contractGas(context: WriteContext, call: {
38
+ address: Address;
39
+ abi: Abi;
40
+ functionName: string;
41
+ args: readonly unknown[];
42
+ }, kind: TxKind): Promise<bigint>;
43
+ /** The gas limit for a plain value transfer, under the same ceiling rule as a contract write. */
44
+ export declare function valueGas(context: WriteContext, transfer: {
45
+ to: Address;
46
+ value: bigint;
47
+ }, kind: TxKind): Promise<bigint>;
@@ -0,0 +1,52 @@
1
+ import type { Address, Hex, OwnerAgentHistory } from "../protocol/index.js";
2
+ import { capabilityRegistryAbi } from "./abis.js";
3
+ import type { Deployment } from "./deployment.js";
4
+ import type { LogClient } from "./logs.js";
5
+ /**
6
+ * §14.6 PREVIOUSLY_REVOKED input. Built only from CapabilityRevoked and AgentRevoked events for exactly
7
+ * this (owner, agentId) pair. Topic filters narrow the query; the explicit comparison below guards against
8
+ * a provider that ignores them. Never consults reputation or access telemetry.
9
+ */
10
+ /** A log client that can also read a contract view — viem's PublicClient is one. */
11
+ export interface HistoryClient extends LogClient {
12
+ readContract?(parameters: {
13
+ address: Address;
14
+ abi: typeof capabilityRegistryAbi;
15
+ functionName: "agentEpoch" | "activeCapabilityIds";
16
+ args: readonly [Address, Hex];
17
+ }): Promise<unknown>;
18
+ }
19
+ /**
20
+ * Where the last successful scan stopped for one (owner, agentId) pair (R4-9). A cache of chain
21
+ * facts, never an authority: the caller supplies load/save (the CLI keeps it at
22
+ * `state/history/<agentId>.json`, keyed on chain id and registry) and a missing, malformed or
23
+ * wrong-chain answer from load() means a full scan, not a guess. `previouslyRevoked` is sticky —
24
+ * nothing on the chain un-revokes — so a saved true answers without any scan at all.
25
+ */
26
+ export interface HistoryScanCursor {
27
+ load(): {
28
+ observedThroughBlock: bigint;
29
+ previouslyRevoked: boolean;
30
+ } | undefined | Promise<{
31
+ observedThroughBlock: bigint;
32
+ previouslyRevoked: boolean;
33
+ } | undefined>;
34
+ save(state: {
35
+ observedThroughBlock: bigint;
36
+ previouslyRevoked: boolean;
37
+ }): void | Promise<void>;
38
+ }
39
+ export declare function ownerHistory(input: {
40
+ client: HistoryClient;
41
+ deployment: Deployment;
42
+ owner: Address;
43
+ agentId: Hex;
44
+ toBlock?: bigint;
45
+ cursor?: HistoryScanCursor;
46
+ /** The window size the scan opens with — the CLI passes the resolved MIDA_LOG_BLOCK_RANGE. */
47
+ maxRange?: bigint;
48
+ /** Called once, before the log scan starts, with an ESTIMATE of the requests it will take. */
49
+ onScan?: (requests: number) => void;
50
+ /** Live progress for each log scan — (completed, planned) requests; planned is the same estimate. */
51
+ onProgress?: (done: number, total: number) => void;
52
+ }): Promise<OwnerAgentHistory>;
@@ -0,0 +1,13 @@
1
+ export * from "./abis.js";
2
+ export * from "./deployment.js";
3
+ export * from "./deployment-fs.js";
4
+ export * from "./logs.js";
5
+ export * from "./history.js";
6
+ export * from "./placements.js";
7
+ export * from "./registry.js";
8
+ export * from "./local.js";
9
+ export * from "./gas.js";
10
+ export * from "./transport.js";
11
+ export * from "./read-scope.js";
12
+ export * from "./writes.js";
13
+ export * from "./sponsored.js";
@@ -0,0 +1,34 @@
1
+ import type { Hex } from "../protocol/index.js";
2
+ import type { Deployment } from "./deployment.js";
3
+ /** Development tooling for tests and the CLI. Never used against a real network. */
4
+ export declare const FOUNDRY_BIN: string;
5
+ /**
6
+ * The contracts/ directory, resolved on first use and cached. It must stay lazy: this module is bundled
7
+ * into the store Worker, where `import.meta.url` is not a parseable URL — evaluating
8
+ * fileURLToPath(new URL(…)) at module scope would throw on startup. The Worker never calls this.
9
+ */
10
+ export declare function CONTRACTS_DIR(): string;
11
+ /** Anvil's public, pre-funded development keys (mnemonic "test test ... junk"). Never use on a real network. */
12
+ export declare const ANVIL_PRIVATE_KEYS: readonly Hex[];
13
+ export interface LocalNode {
14
+ rpcUrl: string;
15
+ hardfork: string;
16
+ stop(): Promise<void>;
17
+ }
18
+ /** Starts a fresh Anvil on a free port. hardfork "default" keeps Anvil's default (Osaka, native P256 at 0x100). */
19
+ export declare function startAnvil(options?: {
20
+ hardfork?: string;
21
+ }): Promise<LocalNode>;
22
+ /**
23
+ * Deploys with contracts/script/Deploy.s.sol, then DeployBatchAnchor.s.sol beside it, and returns the
24
+ * parsed deployment file (batchAnchor + batchAnchorBlock set). Every local Anvil writes the same
25
+ * deployments/31337.json, so deployments from parallel test files are serialized with a directory lock.
26
+ */
27
+ export declare function deployLocal(options: {
28
+ rpcUrl: string;
29
+ privateKey?: Hex;
30
+ }): Promise<Deployment>;
31
+ /** Anvil-only: sets an address balance so generated agent signers can pay gas in local tests. */
32
+ export declare function fundLocal(rpcUrl: string, address: string, wei?: bigint): Promise<void>;
33
+ /** Anvil-only: moves chain time forward and mines one block, for expiry tests. */
34
+ export declare function increaseLocalTime(rpcUrl: string, seconds: bigint): Promise<void>;
@@ -0,0 +1,62 @@
1
+ import type { Address, Hex } from "../protocol/index.js";
2
+ import type { AbiEvent } from "viem";
3
+ /** The largest window an eth_getLogs scan may open with — Quicknode's Monad endpoint serves 1,000-block ranges. */
4
+ export declare const MAX_LOG_BLOCK_RANGE = 1000n;
5
+ /**
6
+ * The window every provider is assumed to accept — Monad's public RPC caps eth_getLogs at 100
7
+ * blocks. The first range refusal drops the rest of a scan, and the refused window, to this size.
8
+ */
9
+ export declare const SAFE_LOG_BLOCK_RANGE = 100n;
10
+ export interface BlockWindow {
11
+ fromBlock: bigint;
12
+ toBlock: bigint;
13
+ }
14
+ export declare function blockWindows(fromBlock: bigint, toBlock: bigint, size?: bigint): BlockWindow[];
15
+ /**
16
+ * The window size a scan opens with, clamped to 1..MAX_LOG_BLOCK_RANGE. `undefined` means the
17
+ * maximum. Callers pass an operator-chosen value through (MIDA_LOG_BLOCK_RANGE); a nonsense one
18
+ * is pulled inside the valid range rather than crashing the scan — the same way an unreadable
19
+ * env value is ignored instead of refused.
20
+ */
21
+ export declare function clampLogRange(maxRange: bigint | undefined): bigint;
22
+ export interface DecodedLog {
23
+ args: Record<string, unknown>;
24
+ blockNumber: bigint | null;
25
+ transactionHash: Hex | null;
26
+ logIndex: number | null;
27
+ /** The index of the transaction that emitted this log inside its block (in-13 M-5). */
28
+ transactionIndex: number | null;
29
+ }
30
+ /** Structural subset of viem's PublicClient used for log scans, so tests can pass a recording fake. */
31
+ export interface LogClient {
32
+ getBlockNumber(parameters?: {
33
+ cacheTime?: number;
34
+ }): Promise<bigint>;
35
+ getLogs(parameters: {
36
+ address: Address;
37
+ event: AbiEvent;
38
+ args?: Record<string, unknown>;
39
+ fromBlock: bigint;
40
+ toBlock: bigint;
41
+ strict: true;
42
+ }): Promise<readonly unknown[]>;
43
+ }
44
+ export interface LogScanOptions {
45
+ /** The window size the scan opens with — clamped to 1..MAX_LOG_BLOCK_RANGE, default the maximum. */
46
+ maxRange?: bigint;
47
+ /**
48
+ * Scan progress as (completed, planned) requests. `total` grows when a range refusal splits a
49
+ * window into SAFE pieces, so it is an estimate, not a promise. Called at most about once per
50
+ * 5% of the plan, and always once at the end with (total, total).
51
+ */
52
+ onProgress?: (done: number, total: number) => void;
53
+ }
54
+ export declare function getLogsChunked(client: LogClient, parameters: {
55
+ address: Address;
56
+ event: AbiEvent;
57
+ args?: Record<string, unknown>;
58
+ fromBlock: bigint;
59
+ toBlock?: bigint;
60
+ }, options?: LogScanOptions): Promise<DecodedLog[]>;
61
+ /** How many windows are in flight at once. */
62
+ export declare const LOG_SCAN_CONCURRENCY = 8;
@@ -0,0 +1,92 @@
1
+ import type { Address, Hex } from "../protocol/index.js";
2
+ import type { Deployment } from "./deployment.js";
3
+ import type { LogClient } from "./logs.js";
4
+ /**
5
+ * Where Monad placed one save: the block its anchoring event sits in, the anchoring
6
+ * transaction's index inside that block, and the save's own position inside the transaction
7
+ * — a batch position for a batched save, the log's index in the block for a direct one.
8
+ * `record.createdAt` already carries Monad's timestamp (the contract stores
9
+ * `block.timestamp`); the placement fields exist only in the event log, so a reader that
10
+ * needs them scans it. `index` is a different unit in each lane and must never be compared
11
+ * across them — one transaction never emits both events (in-13 M-5).
12
+ */
13
+ export interface RecordPlacement {
14
+ block: bigint;
15
+ /** The anchoring transaction's index inside the block — absent when the log carried none. */
16
+ transaction?: number;
17
+ index: number;
18
+ }
19
+ /**
20
+ * contextId → the placement of its ContextRegistered log. The owner — and optionally the
21
+ * namespace — ride the indexed topics, so one bounded scan answers for every record a read is
22
+ * about to return. Keys are lower-cased contextIds; a record whose log is somehow absent simply
23
+ * has no entry — its callers still order on the chain-stored createdAt and degrade only the
24
+ * same-second tie-break, never the timestamp itself.
25
+ */
26
+ export declare function recordPlacements(input: {
27
+ client: LogClient;
28
+ deployment: Deployment;
29
+ owner: Address;
30
+ namespaceId?: Hex;
31
+ toBlock?: bigint;
32
+ maxRange?: bigint;
33
+ }): Promise<Map<string, RecordPlacement>>;
34
+ /** The narrow slice of viem's PublicClient a block-time lookup needs. */
35
+ export interface BlockClient {
36
+ getBlock(parameters: {
37
+ blockNumber: bigint;
38
+ }): Promise<{
39
+ timestamp: bigint;
40
+ }>;
41
+ }
42
+ /**
43
+ * The narrow slice of viem's PublicClient the tie-break scan needs: log windows, the latest
44
+ * block, and — since in-12 N-5 corrects the estimate with the chain's own observed block rate —
45
+ * a single numbered block's timestamp. A missing block answers `null`, never a throw.
46
+ */
47
+ export interface TieScanClient extends LogClient {
48
+ getBlock(parameters?: {
49
+ blockTag: "latest";
50
+ } | {
51
+ blockNumber: bigint;
52
+ }): Promise<{
53
+ number: bigint | null;
54
+ timestamp: bigint;
55
+ } | null>;
56
+ }
57
+ /**
58
+ * Half the blocks a same-second tie scans around its corrected estimate — 50 each side, so the
59
+ * window is at most SAFE_LOG_BLOCK_RANGE (100) blocks wide and never exceeds the public RPC's
60
+ * getLogs cap. Kept exported under its in-9 name.
61
+ */
62
+ export declare const PLACEMENT_TIE_SPAN: bigint;
63
+ /**
64
+ * contextId → placement, recovered ONLY for records whose chain stamps share a second — the one
65
+ * case where `createdAt` alone cannot order them (in-9 R-1). The event sits in a block whose
66
+ * timestamp is that second; finding it costs at most three getBlock calls per tied second —
67
+ * the shared head, an estimate probe, and one correction — because the observed rate between
68
+ * two real blocks is a better ruler than viem's static blockTime (in-12 N-5: a chain running
69
+ * 410–500 ms instead of its nominal 400, or recovering from a stall, threw the old estimate
70
+ * tens of thousands of blocks off). The scan itself opens a window of at most 100 blocks around
71
+ * the corrected center — never wider, so the public RPC's getLogs cap cannot refuse it —
72
+ * and overlapping windows merge then re-slice into ≤100-block requests.
73
+ *
74
+ * Completeness is per tied second, not per record: if the scan finds only SOME of the records
75
+ * sharing a second, that second keeps no placements at all — a found record ordering before an
76
+ * unfound one on a partial answer is exactly the wrong-but-plausible outcome this exists to
77
+ * prevent, so the whole tie falls back to the contextId order together.
78
+ */
79
+ export declare function recordPlacementsNear(input: {
80
+ client: TieScanClient;
81
+ deployment: Deployment;
82
+ owner: Address;
83
+ namespaceId?: Hex;
84
+ /** The tied second (the records' shared `createdAt`) → the contextIds stamped with it. */
85
+ tied: ReadonlyMap<bigint, readonly Hex[]>;
86
+ }): Promise<Map<string, RecordPlacement>>;
87
+ /**
88
+ * One `getBlock` per distinct block, shared across a whole call — a read that anchors many saves
89
+ * out of a handful of blocks asks once each. The in-flight promise is cached so concurrent
90
+ * callers share the request (and one another's failure).
91
+ */
92
+ export declare function blockTimeCache(client: BlockClient): (block: bigint) => Promise<bigint>;
@@ -0,0 +1,66 @@
1
+ import type { PublicClient } from "viem";
2
+ /**
3
+ * in-9 R-5 — the per-operation read scope.
4
+ *
5
+ * One logical read (a handoff, a whats-new refresh) asks the chain the same questions many
6
+ * times: the agent record for every epoch key, the capability state for every object, the
7
+ * same block for every save it anchored. None of those answers can change inside one
8
+ * operation — the chain either held them when the operation started or the next operation
9
+ * will see the change. So the scope keeps one Map of `(method, args) → the in-flight wire
10
+ * call`, shared by every client built inside the operation: the runtime's own client and
11
+ * each agent's fresh one all hit the same entry, and an answer still on the wire is awaited
12
+ * once, not re-requested.
13
+ *
14
+ * The scope is a snapshot by design and dies with the operation. It is never process-wide:
15
+ * a revocation that lands between two handoffs is read fresh by the second one, because the
16
+ * second handoff builds a new scope.
17
+ *
18
+ * A scope can also carry a deadline: once it passes, no NEW chain read starts for the
19
+ * operation — an abandoned handoff stops spending the shared rate limit instead of finishing
20
+ * a read nobody will ever render. Calls already on the wire are left to finish; only new
21
+ * ones are refused with ReadDeadlineError.
22
+ */
23
+ export interface ReadScope {
24
+ /**
25
+ * signer (lowercase address) → the read-scope token the store last issued for it (in-12 N-10).
26
+ * The token is the store's own HMAC-signed grant — client code never invents one — so this map
27
+ * is only ever filled from response headers, and an entry that has expired is replaced by the
28
+ * next response's token rather than stamped again.
29
+ */
30
+ readonly tokens: Map<string, string>;
31
+ /** `(method, serialized args)` → the one in-flight or settled wire call the operation shares. */
32
+ readonly memo: Map<string, Promise<unknown>>;
33
+ /** epoch ms after which the scope starts no new chain read; absent means no deadline. */
34
+ readonly deadlineAt?: number;
35
+ }
36
+ /** The error a read started after the scope's deadline gets — the operation ran out of time. */
37
+ export declare class ReadDeadlineError extends Error {
38
+ readonly code: "read-deadline";
39
+ constructor();
40
+ }
41
+ /** Walks the cause chain — viem wraps fetch failures — for a scope-deadline refusal. */
42
+ export declare function isReadDeadlineError(error: unknown): boolean;
43
+ export declare function createReadScope(options?: {
44
+ deadlineMs?: number;
45
+ }): ReadScope;
46
+ /**
47
+ * The test-visible counter the transport probe is for requests: every memoized call records
48
+ * whether it hit an existing entry or started the wire call. Counters accumulate until reset —
49
+ * measure a window by resetting first.
50
+ */
51
+ export declare const readScopeProbe: {
52
+ hits: number;
53
+ misses: number;
54
+ /** reads refused after the scope's deadline */
55
+ refused: number;
56
+ reset(): void;
57
+ };
58
+ /**
59
+ * A client whose memoized methods share the scope's wire calls. Only the whitelisted read
60
+ * methods are touched; every other property and method passes straight through, so a
61
+ * write-capable client wrapped here still sends, estimates and waits on receipts normally.
62
+ *
63
+ * A FAILED call is evicted from the memo: one transient refusal must not poison every later
64
+ * identical read in the operation — the next one tries again on its own request.
65
+ */
66
+ export declare function memoizedReads(client: PublicClient, scope: ReadScope): PublicClient;
@@ -0,0 +1,16 @@
1
+ import type { AgentRecord, Hex, MidaErrorCode } from "../protocol/index.js";
2
+ import type { PublicClient } from "viem";
3
+ import type { Deployment } from "./deployment.js";
4
+ export interface ChainContext {
5
+ publicClient: PublicClient;
6
+ deployment: Deployment;
7
+ }
8
+ /** Contract custom-error names mapped to protocol codes (§12.6 first, plan decision 3 codes otherwise). */
9
+ export declare const REVERT_CODES: Readonly<Record<string, MidaErrorCode>>;
10
+ export declare function revertNameFromData(data: Hex): string | undefined;
11
+ export declare function revertName(error: unknown): string | undefined;
12
+ /** Returns a MidaError for a known contract revert; any other error is returned unchanged for rethrow. */
13
+ export declare function toMidaError(error: unknown): unknown;
14
+ export declare function readAgentRecord(context: ChainContext, agentId: Hex): Promise<AgentRecord>;
15
+ /** Chain time, not wall-clock time, decides expiry so the API and the contracts use one clock (Part D note). */
16
+ export declare function latestTimestamp(context: ChainContext): Promise<bigint>;
@@ -0,0 +1,93 @@
1
+ import { MidaError } from "../protocol/index.js";
2
+ import type { Abi, Address, Hex, LocalAccount } from "viem";
3
+ import type { Deployment } from "./deployment.js";
4
+ import type { TxKind } from "./gas.js";
5
+ import type { SentReceipt } from "./writes.js";
6
+ /**
7
+ * The account implementation the SDK's first sponsored operation delegates to — Pimlico's
8
+ * Simple7702Account, the default inside `to7702SimpleSmartAccount`. The sponsor endpoint's
9
+ * ALLOWED_IMPLEMENTATIONS must contain this address or every first operation is refused.
10
+ */
11
+ export declare const SPONSORED_IMPLEMENTATION: Address;
12
+ /** Phase 1 — up to the bundler answering with a hash — gets this long; past it the caller may fall back. */
13
+ export declare const SPONSOR_TIMEOUT_MS = 20000;
14
+ /** Phase 2 — waiting for the receipt of an ACCEPTED operation — gets its own, longer deadline. */
15
+ export declare const SPONSOR_RECEIPT_TIMEOUT_MS = 120000;
16
+ /**
17
+ * The gas the bundler's `callGasLimit` does NOT count: that field prices the inner execution
18
+ * only, while GAS_CEILINGS were measured as whole-transaction limits — verification,
19
+ * pre-verification and paymaster gas ride on top. Comparing the raw numbers let the sponsored
20
+ * path run tens of thousands of gas more permissive than the self-paid one (M3-D6 item 4), so
21
+ * the kind's ceiling minus this overhead is the bound `callGasLimit` is checked against —
22
+ * the same effective whole-transaction ceiling both paths enforce.
23
+ */
24
+ export declare const CALL_OVERHEAD_GAS = 40000n;
25
+ /** Once the receipt wait runs this long the owner hears one progress line — silence is not pending. */
26
+ export declare const SPONSOR_RECEIPT_NOTICE_MS = 15000;
27
+ /**
28
+ * Every way a sponsored send can fail before the operation lands: refusal, unreachable endpoint,
29
+ * timeout, malformed answer. `sendContract` falls back to self-pay only on this type — an operation
30
+ * that was included and reverted is a different outcome (the sponsor DID pay) and is never retried.
31
+ */
32
+ export declare class SponsorDidNotPay extends MidaError {
33
+ /** The short human reason, surfaced on the fallback progress line. */
34
+ readonly reason: string;
35
+ constructor(reason: string);
36
+ }
37
+ /**
38
+ * The bundler accepted a user operation — a hash exists — but its receipt could not be confirmed
39
+ * inside the receipt deadline. The operation may still land, so this is deliberately NOT
40
+ * SponsorDidNotPay: `sendContract` falls back only on that type, and a self-paid copy of an
41
+ * accepted call is the exact double-send this error exists to prevent.
42
+ */
43
+ export declare class SponsorPending extends MidaError {
44
+ /** The accepted operation's hash — surfaced to the owner so the outcome can be checked. */
45
+ readonly userOpHash: Hex;
46
+ constructor(userOpHash: Hex);
47
+ }
48
+ /**
49
+ * A sponsored send's answer, shaped like the self-paid `SentReceipt`: `transactionHash` is the
50
+ * bundle transaction, `gasLimit` the sum of the operation's gas fields (Monad bills the sponsor
51
+ * for the limit on every field, not the usage), plus the `userOpHash` the bundle carries.
52
+ */
53
+ export type SponsoredReceipt = SentReceipt & {
54
+ userOpHash: Hex;
55
+ };
56
+ export interface SponsoredSender {
57
+ /**
58
+ * Sends one contract call as a sponsored user operation. `kind` carries the per-kind gas
59
+ * ceiling this sender enforces against the bundler's own `callGasLimit` estimate before the
60
+ * operation is sent (M3-D3) — the owner-side `eth_estimateGas` never runs on this path.
61
+ */
62
+ send(call: {
63
+ address: Address;
64
+ abi: Abi;
65
+ functionName: string;
66
+ args: readonly unknown[];
67
+ }, kind: TxKind): Promise<SponsoredReceipt>;
68
+ }
69
+ /**
70
+ * A sender whose gas is paid by the Mida sponsor endpoint while `sender` stays the user's own
71
+ * address — the contracts still see the user as msg.sender. `sponsorUrl` speaks the Pimlico
72
+ * bundler+paymaster JSON-RPC dialect our sponsor worker proxies (EntryPoint v0.8). The user's
73
+ * first operation signs an EIP-7702 authorization for `implementation`; later ones carry none,
74
+ * because the delegation is already on chain.
75
+ */
76
+ export declare function createSponsoredSender(input: {
77
+ sponsorUrl: string;
78
+ rpcUrl: string;
79
+ account: LocalAccount;
80
+ deployment: Deployment;
81
+ /** Override the delegated implementation — must be on the endpoint's allow list. */
82
+ implementation?: Address;
83
+ /** Phase-1 deadline (paymaster + send, up to acceptance), default SPONSOR_TIMEOUT_MS; tests pass less. */
84
+ timeoutMs?: number;
85
+ /** Phase-2 deadline (receipt confirmation after acceptance), default SPONSOR_RECEIPT_TIMEOUT_MS. */
86
+ receiptTimeoutMs?: number;
87
+ /** Delay before the "still waiting" progress line, default SPONSOR_RECEIPT_NOTICE_MS. */
88
+ receiptNoticeMs?: number;
89
+ /** Receipt polling interval; viem's default when unset. */
90
+ pollingIntervalMs?: number;
91
+ /** Progress lines for the human running the command — fires once if the receipt wait runs long. */
92
+ progress?: (line: string) => void;
93
+ }): SponsoredSender;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The chain RPC stayed rate-limited through every retry (250/500/1000 ms). Downstream callers
3
+ * surface this as reason `chain-busy` / code CHAIN_UNAVAILABLE — never "not approved": a busy
4
+ * chain could not be asked, it did not refuse.
5
+ */
6
+ export declare class ChainBusyError extends Error {
7
+ readonly code = "CHAIN_BUSY";
8
+ constructor();
9
+ }
10
+ /**
11
+ * True when a ChainBusyError sits anywhere in the error's cause chain. viem wraps a fetchFn
12
+ * failure in HttpRequestError (and callers may wrap again), so instanceof alone is not enough.
13
+ */
14
+ export declare function isChainBusy(error: unknown): boolean;
15
+ /**
16
+ * What a thrown chain error actually is, so every surface names it honestly (in-11 R-8):
17
+ *
18
+ * - `busy` — the RPC could not answer: stayed rate-limited, answered 408/429/5xx, dropped the
19
+ * connection (an HttpRequestError with no status), timed out, or reported an RPC-level
20
+ * overload code (-32005 limit exceeded / -32603 provider-internal). Waiting and retrying is
21
+ * honest advice — the same set the transport's own retry treats as transient.
22
+ * - `misconfigured` — the RPC answered but the call returned "0x" (no contract at the
23
+ * configured address): a wrong-network rpcUrl or a stale deployment. The fix is
24
+ * MONAD_TESTNET_RPC / network.json, not a retry.
25
+ * - `rpc-auth` — the provider refused the key (HTTP 401/403). The fix is the provider
26
+ * credential, not a retry.
27
+ * - `undefined` — anything else: a real contract answer (a revert), a decode failure, a local
28
+ * fault. Not renamed — callers keep their own fallback rather than lie.
29
+ *
30
+ * The walk also honours codes a store already computed: a CHAIN_UNAVAILABLE /
31
+ * CHAIN_MISCONFIGURED / RPC_AUTH_REJECTED answer classifies the same as the raw transport
32
+ * shape it was wrapped from, so a hosted store's answer and a local call get one name.
33
+ */
34
+ export type ChainErrorKind = "busy" | "misconfigured" | "rpc-auth";
35
+ export declare function chainErrorKind(error: unknown): ChainErrorKind | undefined;
36
+ /**
37
+ * Test seam (in-6 R1/R2): the admission instant of every request the limiter released — one per
38
+ * wire send, so its length is the HTTP request count and its timestamps are what the limiter
39
+ * enforced (the fetch itself dispatches within the same event-loop turn). Module-global; tests
40
+ * call `reset()` and read `sentAt`.
41
+ */
42
+ export declare const rpcTransportProbe: {
43
+ sentAt: number[];
44
+ reset(): void;
45
+ };
46
+ /**
47
+ * The transport to put in every public and wallet client: `rpcTransport(url)` in place of
48
+ * `http(url)`. The rate limit key is the URL's origin, so two transports aimed at one host share
49
+ * one bucket — inside ONE process. The bucket is per process, not global: a `mida` CLI running
50
+ * beside the daemon has its own, so N processes can still reach N× the host's allowance (in-11
51
+ * R-15 — the old comment claimed they could not).
52
+ */
53
+ export declare function rpcTransport(url: string): import("viem").HttpTransport<undefined, false>;