@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.
- package/LICENSE +21 -0
- package/README.md +26 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7115 -0
- package/dist/types/api/app.d.ts +99 -0
- package/dist/types/api/auth-pure.d.ts +51 -0
- package/dist/types/api/auth.d.ts +17 -0
- package/dist/types/api/authorize.d.ts +24 -0
- package/dist/types/api/batch-deny.d.ts +22 -0
- package/dist/types/api/batch-routes.d.ts +69 -0
- package/dist/types/api/batch-store.d.ts +96 -0
- package/dist/types/api/batcher.d.ts +220 -0
- package/dist/types/api/browser.d.ts +14 -0
- package/dist/types/api/chain-budget.d.ts +61 -0
- package/dist/types/api/chain-views.d.ts +87 -0
- package/dist/types/api/client.d.ts +295 -0
- package/dist/types/api/deny-overlay.d.ts +81 -0
- package/dist/types/api/errors.d.ts +30 -0
- package/dist/types/api/file-stores.d.ts +7 -0
- package/dist/types/api/index.d.ts +17 -0
- package/dist/types/api/secure-fs.d.ts +12 -0
- package/dist/types/api/store.d.ts +51 -0
- package/dist/types/api/stores.d.ts +171 -0
- package/dist/types/api/verify-assertion.d.ts +22 -0
- package/dist/types/api/wire.d.ts +27 -0
- package/dist/types/chain/abis.d.ts +2125 -0
- package/dist/types/chain/browser.d.ts +20 -0
- package/dist/types/chain/deployment-fs.d.ts +15 -0
- package/dist/types/chain/deployment.d.ts +33 -0
- package/dist/types/chain/deployments.generated.d.ts +1 -0
- package/dist/types/chain/gas.d.ts +47 -0
- package/dist/types/chain/history.d.ts +52 -0
- package/dist/types/chain/index.d.ts +13 -0
- package/dist/types/chain/local.d.ts +34 -0
- package/dist/types/chain/logs.d.ts +62 -0
- package/dist/types/chain/placements.d.ts +92 -0
- package/dist/types/chain/read-scope.d.ts +66 -0
- package/dist/types/chain/registry.d.ts +16 -0
- package/dist/types/chain/sponsored.d.ts +93 -0
- package/dist/types/chain/transport.d.ts +53 -0
- package/dist/types/chain/writes.d.ts +165 -0
- package/dist/types/crypto/aead.d.ts +5 -0
- package/dist/types/crypto/bytes.d.ts +4 -0
- package/dist/types/crypto/derive.d.ts +20 -0
- package/dist/types/crypto/index.d.ts +6 -0
- package/dist/types/crypto/object.d.ts +42 -0
- package/dist/types/crypto/payload.d.ts +21 -0
- package/dist/types/crypto/wraps.d.ts +36 -0
- package/dist/types/grant-advisor/advise.d.ts +23 -0
- package/dist/types/grant-advisor/authority.d.ts +44 -0
- package/dist/types/grant-advisor/index.d.ts +5 -0
- package/dist/types/grant-advisor/manifest.d.ts +66 -0
- package/dist/types/grant-advisor/policy.d.ts +91 -0
- package/dist/types/grant-advisor/signatures.d.ts +10 -0
- package/dist/types/mida-context-sdk/daemon.d.ts +27 -0
- package/dist/types/mida-context-sdk/errors.d.ts +27 -0
- package/dist/types/mida-context-sdk/index.d.ts +5 -0
- package/dist/types/mida-context-sdk/local.d.ts +40 -0
- package/dist/types/mida-context-sdk/mida.d.ts +65 -0
- package/dist/types/mida-context-sdk/transport.d.ts +137 -0
- package/dist/types/protocol/batch.d.ts +161 -0
- package/dist/types/protocol/constants.d.ts +56 -0
- package/dist/types/protocol/errors.d.ts +15 -0
- package/dist/types/protocol/ids.d.ts +72 -0
- package/dist/types/protocol/index.d.ts +11 -0
- package/dist/types/protocol/namespaces.d.ts +15 -0
- package/dist/types/protocol/owner-link.d.ts +146 -0
- package/dist/types/protocol/typed-data.d.ts +365 -0
- package/dist/types/protocol/types.d.ts +215 -0
- package/dist/types/protocol/webauthn-assertion.d.ts +29 -0
- package/dist/types/protocol/wire.d.ts +8 -0
- package/dist/types/sdk/agent.d.ts +279 -0
- package/dist/types/sdk/batched.d.ts +68 -0
- package/dist/types/sdk/connect.d.ts +88 -0
- package/dist/types/sdk/index.d.ts +7 -0
- package/dist/types/sdk/request-store.d.ts +26 -0
- package/dist/types/storage/index.d.ts +23 -0
- 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>;
|