@opendatalabs/vana-sdk 3.17.0 → 3.18.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/dist/direct/personal-server-read.cjs +20 -1
- package/dist/direct/personal-server-read.cjs.map +1 -1
- package/dist/direct/personal-server-read.js +23 -1
- package/dist/direct/personal-server-read.js.map +1 -1
- package/dist/errors.cjs +27 -0
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +67 -0
- package/dist/errors.js +24 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.browser.d.ts +2 -1
- package/dist/index.browser.js +724 -309
- package/dist/index.browser.js.map +4 -4
- package/dist/index.node.cjs +736 -311
- package/dist/index.node.cjs.map +4 -4
- package/dist/index.node.d.ts +2 -1
- package/dist/index.node.js +724 -309
- package/dist/index.node.js.map +4 -4
- package/dist/protocol/data-point-deletion.cjs +220 -0
- package/dist/protocol/data-point-deletion.cjs.map +1 -0
- package/dist/protocol/data-point-deletion.d.ts +180 -0
- package/dist/protocol/data-point-deletion.js +193 -0
- package/dist/protocol/data-point-deletion.js.map +1 -0
- package/dist/protocol/data-point-deletion.test.d.ts +1 -0
- package/dist/protocol/gateway.cjs +145 -32
- package/dist/protocol/gateway.cjs.map +1 -1
- package/dist/protocol/gateway.d.ts +42 -3
- package/dist/protocol/gateway.js +156 -32
- package/dist/protocol/gateway.js.map +1 -1
- package/dist/protocol/personal-server-data.cjs +20 -1
- package/dist/protocol/personal-server-data.cjs.map +1 -1
- package/dist/protocol/personal-server-data.js +23 -1
- package/dist/protocol/personal-server-data.js.map +1 -1
- package/dist/storage/index.cjs.map +1 -1
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/providers/vana-storage.cjs +66 -0
- package/dist/storage/providers/vana-storage.cjs.map +1 -1
- package/dist/storage/providers/vana-storage.d.ts +26 -0
- package/dist/storage/providers/vana-storage.js +66 -0
- package/dist/storage/providers/vana-storage.js.map +1 -1
- package/dist/utils/response-body.cjs +46 -0
- package/dist/utils/response-body.cjs.map +1 -0
- package/dist/utils/response-body.d.ts +26 -0
- package/dist/utils/response-body.js +20 -0
- package/dist/utils/response-body.js.map +1 -0
- package/dist/utils/response-body.test.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/protocol/data-point-deletion.ts"],"sourcesContent":["/**\n * Data point deletion (tombstone) helpers for DataRegistryV2.\n *\n * A deletion is an owner-signed `AddData` for version `current + 1` whose\n * `dataHash` / `metadataHash` are the protocol-wide tombstone constants below.\n * The gateway records the tombstone version and marks the row `deletedAt`;\n * the encrypted blobs in vana-storage are removed afterwards as a best-effort\n * follow-up. The tombstone is the durable fact; blob removal is cleanup.\n *\n * The typed-data definition is exactly {@link ADD_DATA_TYPES} against\n * {@link dataRegistryDomain} -- the same signature the registration path uses,\n * so a verifier cannot tell a deletion apart from a registration except by\n * the hashes.\n *\n * @category Protocol\n */\n\nimport {\n isAddress,\n keccak256,\n maxUint256,\n stringToBytes,\n type Account,\n type Address,\n type Hex,\n type TypedDataDefinition,\n} from \"viem\";\nimport { DataPointDeletedError, DataPointNotFoundError } from \"../errors\";\nimport { isPlainObject } from \"../utils/response-body\";\nimport {\n ADD_DATA_TYPES,\n dataRegistryDomain,\n type AddDataMessage,\n type DataPortabilityGatewayConfig,\n} from \"./eip712\";\nimport type {\n DeleteDataPointResult as GatewayDeleteDataPointResult,\n GatewayClient,\n} from \"./gateway\";\nimport { deriveDataPointId } from \"./lineage\";\n\n/** UTF-8 preimage of {@link TOMBSTONE_DATA_HASH}. */\nexport const TOMBSTONE_DATA_HASH_PREIMAGE = \"vana.data-point.tombstone.v1\";\n/** UTF-8 preimage of {@link TOMBSTONE_METADATA_HASH}. */\nexport const TOMBSTONE_METADATA_HASH_PREIMAGE =\n \"vana.data-point.tombstone.metadata.v1\";\n\n/**\n * `keccak256(utf8(\"vana.data-point.tombstone.v1\"))` -- the `dataHash` a\n * deletion AddData carries. Single source of truth shared with the gateway\n * and Personal Server; never change without a new `.vN` preimage.\n */\nexport const TOMBSTONE_DATA_HASH =\n \"0x30c45ee72fe56d1927701316925ab7ceacd3b6f9267061735d59396f075c6222\" as const;\n\n/**\n * `keccak256(utf8(\"vana.data-point.tombstone.metadata.v1\"))` -- the\n * `metadataHash` a deletion AddData carries.\n */\nexport const TOMBSTONE_METADATA_HASH =\n \"0xc5255a141acd6a2ae55971b62c0a85977c2511989dc114ad2abc2b7644f57d90\" as const;\n\n/**\n * Recompute a tombstone hash from its preimage. Exposed so tests and\n * downstream services can prove the pinned constants match the contract.\n */\nexport function computeTombstoneHash(preimage: string): Hex {\n return keccak256(stringToBytes(preimage));\n}\n\n/** True when the pair of hashes is exactly the tombstone pair. */\nexport function isTombstoneHashes(\n dataHash: string | undefined,\n metadataHash: string | undefined,\n): boolean {\n return (\n typeof dataHash === \"string\" &&\n typeof metadataHash === \"string\" &&\n dataHash.toLowerCase() === TOMBSTONE_DATA_HASH &&\n metadataHash.toLowerCase() === TOMBSTONE_METADATA_HASH\n );\n}\n\n/**\n * Detect a deleted data point from any record-shaped value: a non-null\n * `deletedAt`, or the tombstone hash pair. Accepts `unknown` so read\n * helpers can run it on a raw JSON body before trusting the payload.\n */\nexport function isDataPointTombstone(value: unknown): boolean {\n if (!isPlainObject(value)) return false;\n if (typeof value[\"deletedAt\"] === \"string\") return true;\n return isTombstoneHashes(\n typeof value[\"dataHash\"] === \"string\" ? value[\"dataHash\"] : undefined,\n typeof value[\"metadataHash\"] === \"string\"\n ? value[\"metadataHash\"]\n : undefined,\n );\n}\n\n/**\n * Read `deletedAt` off any value a gateway or Personal Server might hand\n * back (a record, an error body, `null`, an array, ...). Returns `null`\n * unless it is a string, so every `DataPointDeletedError` carries the same\n * `deletedAt` semantics regardless of which read path raised it.\n */\nexport function tombstoneDeletedAt(value: unknown): string | null {\n if (!isPlainObject(value)) return null;\n const deletedAt = value[\"deletedAt\"];\n return typeof deletedAt === \"string\" ? deletedAt : null;\n}\n\nexport type DataPointDeletionTypedData = TypedDataDefinition<\n typeof ADD_DATA_TYPES,\n \"AddData\"\n> & {\n message: AddDataMessage;\n};\n\nexport interface DataPointDeletionSigner {\n /** The data point owner. Must match `AddData.ownerAddress`. */\n address: Address;\n signTypedData(typedData: DataPointDeletionTypedData): Promise<Hex> | Hex;\n}\n\nexport interface ViemDataPointDeletionWalletClient {\n account?: Account | Address | null;\n signTypedData(\n typedData: DataPointDeletionTypedData & { account?: Account | Address },\n ): Promise<Hex>;\n}\n\nexport type ViemDataPointDeletionSignerSource =\n | DataPointDeletionSigner\n | ViemDataPointDeletionWalletClient;\n\nexport interface BuildDataPointDeletionTypedDataInput {\n ownerAddress: Address;\n scope: string;\n /**\n * The version the tombstone is written at -- `current + 1`. This is the\n * `AddData.expectedVersion` field; the gateway rejects with 409 unless it\n * is strictly greater than the stored version.\n */\n expectedVersion: bigint;\n config: DataPortabilityGatewayConfig;\n}\n\nexport interface BuildDataPointDeletionSignatureInput {\n signer: DataPointDeletionSigner;\n scope: string;\n /** See {@link BuildDataPointDeletionTypedDataInput.expectedVersion}. */\n expectedVersion: bigint;\n config: DataPortabilityGatewayConfig;\n}\n\nexport interface DataPointDeletionSignature {\n signature: Hex;\n signerAddress: Address;\n typedData: DataPointDeletionTypedData;\n}\n\nfunction assertAddress(value: string, name: string): void {\n if (!isAddress(value)) {\n throw new Error(`${name} must be a valid EVM address`);\n }\n}\n\n// `AddData.expectedVersion` is a uint256 on the contract. A bigint has no\n// such bound, so every version the SDK signs or increments is checked here\n// rather than trusting the TypeScript type.\nfunction assertUint256(value: bigint, name: string): void {\n if (value < 0n || value > maxUint256) {\n throw new Error(`${name} must fit in uint256, got ${value}`);\n }\n}\n\nfunction getAccountAddress(\n account: Account | Address | null | undefined,\n): Address | undefined {\n if (!account) return undefined;\n return typeof account === \"string\" ? account : account.address;\n}\n\nfunction isDataPointDeletionSigner(\n source: ViemDataPointDeletionSignerSource,\n): source is DataPointDeletionSigner {\n return \"address\" in source && typeof source.signTypedData === \"function\";\n}\n\n/**\n * Adapt a viem local account or wallet client to\n * {@link DataPointDeletionSigner}. Mirrors\n * `createViemPersonalServerRegistrationSigner`.\n */\nexport function createViemDataPointDeletionSigner(\n source: ViemDataPointDeletionSignerSource,\n options: { account?: Account | Address } = {},\n): DataPointDeletionSigner {\n if (isDataPointDeletionSigner(source)) {\n return source;\n }\n\n const accountAddress =\n getAccountAddress(options.account) ?? getAccountAddress(source.account);\n\n if (accountAddress) {\n return {\n address: accountAddress,\n signTypedData: (typedData) =>\n source.signTypedData({\n ...typedData,\n account: options.account ?? source.account ?? accountAddress,\n }),\n };\n }\n\n throw new Error(\n \"Viem wallet client requires an account option or account property\",\n );\n}\n\n/**\n * Build the exact `AddData` typed data a deletion signs: the tombstone hash\n * pair at `expectedVersion`, against the DataRegistryV2 domain.\n */\nexport function buildDataPointDeletionTypedData(\n input: BuildDataPointDeletionTypedDataInput,\n): DataPointDeletionTypedData {\n assertAddress(input.ownerAddress, \"ownerAddress\");\n if (input.scope.length === 0) {\n throw new Error(\"scope must be a non-empty string\");\n }\n if (input.expectedVersion <= 0n) {\n throw new Error(\"expectedVersion must be a positive version number\");\n }\n assertUint256(input.expectedVersion, \"expectedVersion\");\n\n return {\n domain: dataRegistryDomain(input.config),\n types: ADD_DATA_TYPES,\n primaryType: \"AddData\",\n message: {\n ownerAddress: input.ownerAddress,\n scope: input.scope,\n dataHash: TOMBSTONE_DATA_HASH,\n metadataHash: TOMBSTONE_METADATA_HASH,\n expectedVersion: input.expectedVersion,\n },\n };\n}\n\n/** Sign a deletion `AddData` with the owner's signer. */\nexport async function buildDataPointDeletionSignature(\n input: BuildDataPointDeletionSignatureInput,\n): Promise<DataPointDeletionSignature> {\n const typedData = buildDataPointDeletionTypedData({\n ownerAddress: input.signer.address,\n scope: input.scope,\n expectedVersion: input.expectedVersion,\n config: input.config,\n });\n const signature = await input.signer.signTypedData(typedData);\n\n return {\n signature,\n signerAddress: input.signer.address,\n typedData,\n };\n}\n\n/**\n * Outcome of a scope-wide blob delete. `VanaStorage.deleteScope` returns a\n * superset of this shape.\n */\nexport interface DataPointBlobDeleteResult {\n count: number;\n totalBytes: number;\n}\n\n/**\n * The storage half of a deletion -- structurally satisfied by `VanaStorage`.\n * Removes every version's blob under `(owner, scope)`.\n */\nexport interface DataPointBlobStore {\n deleteScope(\n ownerAddress: Address,\n scope: string,\n ): Promise<DataPointBlobDeleteResult>;\n}\n\nexport interface DeleteDataPointInput {\n /** Gateway client -- only `getDataPoint` and `deleteDataPoint` are used. */\n gateway: Pick<GatewayClient, \"getDataPoint\" | \"deleteDataPoint\">;\n /** Blob store for the best-effort cleanup after the tombstone lands. */\n storage: DataPointBlobStore;\n /** Owner signer. `signer.address` is the data point owner. */\n signer: DataPointDeletionSigner;\n scope: string;\n config: DataPortabilityGatewayConfig;\n /**\n * Skip the gateway lookup and treat this as the current version. Use when\n * the caller already holds a fresh `DataPointRecord.expectedVersion`;\n * omit to let the SDK fetch it.\n */\n currentVersion?: bigint;\n}\n\ninterface DataPointDeletionBase {\n dataPointId: Hex;\n ownerAddress: Address;\n scope: string;\n /** Decimal-string uint256 of the tombstone version (`current + 1`). */\n version: string;\n signature: Hex;\n /** The gateway's 200 body for the tombstone. */\n tombstone: GatewayDeleteDataPointResult;\n}\n\n/** Both the gateway tombstone and the blob cleanup succeeded. */\nexport interface DataPointDeletedResult extends DataPointDeletionBase {\n status: \"deleted\";\n storage: DataPointBlobDeleteResult;\n}\n\n/**\n * The gateway recorded the tombstone (durable, the data point is deleted)\n * but the blob cleanup failed. Retry `storage.deleteScope(owner, scope)`\n * later; the tombstone does not need to be re-signed.\n */\nexport interface DataPointDeletionPartialResult extends DataPointDeletionBase {\n status: \"partial\";\n storageError: Error;\n}\n\nexport type DataPointDeletionResult =\n | DataPointDeletedResult\n | DataPointDeletionPartialResult;\n\nfunction toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value));\n}\n\n// The wire type says decimal-string uint256, but the type system cannot\n// enforce that at runtime: refuse anything else here rather than letting\n// `BigInt()` throw a bare SyntaxError (or, worse, sign for a bogus version).\nfunction parseExpectedVersion(value: unknown): bigint {\n if (typeof value !== \"string\" || !/^\\d+$/.test(value)) {\n throw new Error(\n `Gateway returned a malformed expectedVersion: ${JSON.stringify(value)}`,\n );\n }\n const parsed = BigInt(value);\n assertUint256(parsed, \"Gateway expectedVersion\");\n return parsed;\n}\n\n/**\n * Delete a data point end to end. Symmetric with `registerDataPoint`.\n *\n * Order, deliberately:\n * 1. fetch the current version from the gateway (unless supplied),\n * 2. sign the tombstone `AddData` for `current + 1`,\n * 3. `DELETE /v1/data/:dataPointId` on the gateway,\n * 4. delete every blob under `(owner, scope)` in vana-storage.\n *\n * Steps 1-3 throw on failure ({@link DataPointNotFoundError},\n * {@link DataPointDeletedError}, `DataPointVersionConflictError`, or a\n * generic gateway error). A step-4 failure does NOT throw: the tombstone is\n * already the durable fact, so the result comes back with\n * `status: \"partial\"` and the storage error attached.\n */\nexport async function deleteDataPoint(\n input: DeleteDataPointInput,\n): Promise<DataPointDeletionResult> {\n const ownerAddress = input.signer.address;\n const dataPointId = deriveDataPointId(ownerAddress, input.scope);\n\n let currentVersion = input.currentVersion;\n if (currentVersion === undefined) {\n const record = await input.gateway.getDataPoint(dataPointId);\n if (record === null) {\n throw new DataPointNotFoundError(\n `No data point registered for scope '${input.scope}' owned by ${ownerAddress}`,\n { dataPointId, scope: input.scope, ownerAddress },\n );\n }\n if (isDataPointTombstone(record)) {\n throw new DataPointDeletedError(\n `Data point ${dataPointId} (scope '${input.scope}') is already deleted`,\n {\n dataPointId,\n scope: input.scope,\n ownerAddress,\n deletedAt: tombstoneDeletedAt(record),\n },\n );\n }\n currentVersion = parseExpectedVersion(record.expectedVersion);\n }\n\n // Whichever source supplied it, `current + 1` must itself be a uint256:\n // a negative current version would sign for 0 or wrap, and\n // 2^256 - 1 cannot be incremented at all.\n if (currentVersion < 0n || currentVersion >= maxUint256) {\n throw new Error(\n `currentVersion ${currentVersion} cannot be incremented to a uint256 tombstone version`,\n );\n }\n const tombstoneVersion = currentVersion + 1n;\n const signed = await buildDataPointDeletionSignature({\n signer: input.signer,\n scope: input.scope,\n expectedVersion: tombstoneVersion,\n config: input.config,\n });\n\n const tombstone = await input.gateway.deleteDataPoint({\n ownerAddress,\n scope: input.scope,\n expectedVersion: tombstoneVersion.toString(),\n signature: signed.signature,\n });\n\n const base: DataPointDeletionBase = {\n dataPointId,\n ownerAddress,\n scope: input.scope,\n version: tombstoneVersion.toString(),\n signature: signed.signature,\n tombstone,\n };\n\n try {\n const storage = await input.storage.deleteScope(ownerAddress, input.scope);\n return { ...base, status: \"deleted\", storage };\n } catch (cause) {\n return { ...base, status: \"partial\", storageError: toError(cause) };\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -22,6 +22,10 @@ __export(gateway_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(gateway_exports);
|
|
24
24
|
var import_scope_actions = require("./scope-actions");
|
|
25
|
+
var import_errors = require("../errors");
|
|
26
|
+
var import_data_point_deletion = require("./data-point-deletion");
|
|
27
|
+
var import_lineage = require("./lineage");
|
|
28
|
+
var import_response_body = require("../utils/response-body");
|
|
25
29
|
function withGrantPermissions(grant) {
|
|
26
30
|
const stripped = { ...grant };
|
|
27
31
|
delete stripped.permissions;
|
|
@@ -34,14 +38,44 @@ function withGrantPermissions(grant) {
|
|
|
34
38
|
}
|
|
35
39
|
function createGatewayClient(baseUrl) {
|
|
36
40
|
const base = baseUrl.replace(/\/+$/, "");
|
|
41
|
+
function malformedBody(res) {
|
|
42
|
+
return new Error(
|
|
43
|
+
`Gateway error: ${res.status} malformed response body (expected a JSON envelope)`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
async function readEnvelope(res) {
|
|
47
|
+
const envelope = await (0, import_response_body.readJsonValue)(res);
|
|
48
|
+
if (!(0, import_response_body.isPlainObject)(envelope) || !("data" in envelope)) {
|
|
49
|
+
throw malformedBody(res);
|
|
50
|
+
}
|
|
51
|
+
return envelope;
|
|
52
|
+
}
|
|
37
53
|
async function unwrapEnvelope(res) {
|
|
38
|
-
|
|
39
|
-
return envelope.data;
|
|
54
|
+
return (await readEnvelope(res)).data;
|
|
40
55
|
}
|
|
41
56
|
function getMutationId(body, key) {
|
|
57
|
+
if (!(0, import_response_body.isPlainObject)(body)) return void 0;
|
|
42
58
|
const value = body[key] ?? body["id"];
|
|
43
59
|
return typeof value === "string" ? value : void 0;
|
|
44
60
|
}
|
|
61
|
+
async function readBody(res) {
|
|
62
|
+
const raw = await (0, import_response_body.readJsonObject)(res);
|
|
63
|
+
const data = raw["data"];
|
|
64
|
+
if ((0, import_response_body.isPlainObject)(data) && "proof" in raw) {
|
|
65
|
+
return data;
|
|
66
|
+
}
|
|
67
|
+
return raw;
|
|
68
|
+
}
|
|
69
|
+
function stringOrUndefined(value) {
|
|
70
|
+
return typeof value === "string" ? value : void 0;
|
|
71
|
+
}
|
|
72
|
+
async function deletedError(res, details) {
|
|
73
|
+
const body = await readBody(res);
|
|
74
|
+
return new import_errors.DataPointDeletedError(
|
|
75
|
+
`Data point ${details.dataPointId ?? details.scope ?? ""} has been deleted`,
|
|
76
|
+
{ ...details, deletedAt: (0, import_data_point_deletion.tombstoneDeletedAt)(body) }
|
|
77
|
+
);
|
|
78
|
+
}
|
|
45
79
|
return {
|
|
46
80
|
async isRegisteredBuilder(address) {
|
|
47
81
|
const builder = await this.getBuilder(address);
|
|
@@ -98,13 +132,29 @@ function createGatewayClient(baseUrl) {
|
|
|
98
132
|
}
|
|
99
133
|
return await res.json();
|
|
100
134
|
},
|
|
101
|
-
async getDataPoint(dataPointId) {
|
|
102
|
-
const
|
|
135
|
+
async getDataPoint(dataPointId, options) {
|
|
136
|
+
const query = options?.includeDeleted ? "?includeDeleted=true" : "";
|
|
137
|
+
const res = await fetch(`${base}/v1/data/${dataPointId}${query}`);
|
|
103
138
|
if (res.status === 404) return null;
|
|
139
|
+
if (res.status === 410) {
|
|
140
|
+
throw await deletedError(res, { dataPointId });
|
|
141
|
+
}
|
|
104
142
|
if (!res.ok) {
|
|
105
143
|
throw new Error(`Gateway error: ${res.status} ${res.statusText}`);
|
|
106
144
|
}
|
|
107
|
-
|
|
145
|
+
const record = await unwrapEnvelope(res);
|
|
146
|
+
if (!options?.includeDeleted && (0, import_data_point_deletion.isDataPointTombstone)(record)) {
|
|
147
|
+
throw new import_errors.DataPointDeletedError(
|
|
148
|
+
`Data point ${dataPointId} has been deleted`,
|
|
149
|
+
{
|
|
150
|
+
dataPointId,
|
|
151
|
+
scope: record.scope,
|
|
152
|
+
ownerAddress: record.ownerAddress,
|
|
153
|
+
deletedAt: (0, import_data_point_deletion.tombstoneDeletedAt)(record)
|
|
154
|
+
}
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
return record;
|
|
108
158
|
},
|
|
109
159
|
async listDataPointsByOwner(owner, cursor, options) {
|
|
110
160
|
const params = new URLSearchParams({ user: owner });
|
|
@@ -117,14 +167,24 @@ function createGatewayClient(baseUrl) {
|
|
|
117
167
|
if (options?.limit !== void 0) {
|
|
118
168
|
params.set("limit", String(options.limit));
|
|
119
169
|
}
|
|
170
|
+
if (options?.includeDeleted) {
|
|
171
|
+
params.set("includeDeleted", "true");
|
|
172
|
+
}
|
|
120
173
|
const res = await fetch(`${base}/v1/data?${params.toString()}`);
|
|
121
174
|
if (!res.ok) {
|
|
122
175
|
throw new Error(`Gateway error: ${res.status} ${res.statusText}`);
|
|
123
176
|
}
|
|
124
|
-
const envelope = await res
|
|
125
|
-
|
|
177
|
+
const envelope = await readEnvelope(res);
|
|
178
|
+
if (!(0, import_response_body.isPlainObject)(envelope.data) || !Array.isArray(envelope.data["dataPoints"])) {
|
|
179
|
+
throw malformedBody(res);
|
|
180
|
+
}
|
|
181
|
+
const rows = envelope.data["dataPoints"];
|
|
182
|
+
const pagination = (0, import_response_body.isPlainObject)(envelope["pagination"]) ? envelope["pagination"] : void 0;
|
|
183
|
+
const rawCursor = pagination?.["nextCursor"];
|
|
184
|
+
const nextCursor = pagination?.["hasMore"] === false || typeof rawCursor !== "string" ? null : rawCursor;
|
|
185
|
+
const dataPoints = options?.includeDeleted ? rows : rows.filter((row) => !(0, import_data_point_deletion.isDataPointTombstone)(row));
|
|
126
186
|
return {
|
|
127
|
-
dataPoints
|
|
187
|
+
dataPoints,
|
|
128
188
|
cursor: nextCursor
|
|
129
189
|
};
|
|
130
190
|
},
|
|
@@ -151,7 +211,7 @@ function createGatewayClient(baseUrl) {
|
|
|
151
211
|
})
|
|
152
212
|
});
|
|
153
213
|
if (res.status === 409) {
|
|
154
|
-
const body2 = await
|
|
214
|
+
const body2 = await (0, import_response_body.readJsonObject)(res);
|
|
155
215
|
return {
|
|
156
216
|
serverId: getMutationId(body2, "serverId"),
|
|
157
217
|
alreadyRegistered: true
|
|
@@ -160,7 +220,7 @@ function createGatewayClient(baseUrl) {
|
|
|
160
220
|
if (!res.ok) {
|
|
161
221
|
throw new Error(`Gateway error: ${res.status} ${res.statusText}`);
|
|
162
222
|
}
|
|
163
|
-
const body = await
|
|
223
|
+
const body = await (0, import_response_body.readJsonObject)(res);
|
|
164
224
|
return {
|
|
165
225
|
serverId: getMutationId(body, "serverId"),
|
|
166
226
|
alreadyRegistered: false
|
|
@@ -181,19 +241,16 @@ function createGatewayClient(baseUrl) {
|
|
|
181
241
|
})
|
|
182
242
|
});
|
|
183
243
|
if (res.status === 409) {
|
|
184
|
-
const body2 = await
|
|
244
|
+
const body2 = await (0, import_response_body.readJsonObject)(res);
|
|
185
245
|
return {
|
|
186
|
-
builderId: getMutationId(
|
|
187
|
-
body2,
|
|
188
|
-
"builderId"
|
|
189
|
-
),
|
|
246
|
+
builderId: getMutationId(body2, "builderId"),
|
|
190
247
|
alreadyRegistered: true
|
|
191
248
|
};
|
|
192
249
|
}
|
|
193
250
|
if (!res.ok) {
|
|
194
251
|
throw new Error(`Gateway error: ${res.status} ${res.statusText}`);
|
|
195
252
|
}
|
|
196
|
-
const body = await
|
|
253
|
+
const body = await (0, import_response_body.readJsonObject)(res);
|
|
197
254
|
return {
|
|
198
255
|
builderId: getMutationId(body, "builderId"),
|
|
199
256
|
alreadyRegistered: false
|
|
@@ -215,17 +272,77 @@ function createGatewayClient(baseUrl) {
|
|
|
215
272
|
})
|
|
216
273
|
});
|
|
217
274
|
if (!res.ok) {
|
|
218
|
-
const body2 = await
|
|
219
|
-
const detail = body2
|
|
275
|
+
const body2 = await (0, import_response_body.readJsonObject)(res);
|
|
276
|
+
const detail = stringOrUndefined(body2["error"]) ?? res.statusText;
|
|
220
277
|
throw new Error(`Gateway error: ${res.status} ${detail}`);
|
|
221
278
|
}
|
|
222
|
-
const body = await
|
|
279
|
+
const body = await (0, import_response_body.readJsonObject)(res);
|
|
223
280
|
return {
|
|
224
|
-
dataPointId: getMutationId(
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
281
|
+
dataPointId: getMutationId(body, "dataPointId"),
|
|
282
|
+
expectedVersion: stringOrUndefined(body["expectedVersion"])
|
|
283
|
+
};
|
|
284
|
+
},
|
|
285
|
+
async deleteDataPoint(params) {
|
|
286
|
+
const dataPointId = (0, import_lineage.deriveDataPointId)(
|
|
287
|
+
params.ownerAddress,
|
|
288
|
+
params.scope
|
|
289
|
+
);
|
|
290
|
+
const details = {
|
|
291
|
+
dataPointId,
|
|
292
|
+
scope: params.scope,
|
|
293
|
+
ownerAddress: params.ownerAddress
|
|
294
|
+
};
|
|
295
|
+
const res = await fetch(`${base}/v1/data/${dataPointId}`, {
|
|
296
|
+
method: "DELETE",
|
|
297
|
+
headers: {
|
|
298
|
+
"Content-Type": "application/json",
|
|
299
|
+
Authorization: `Web3Signed ${params.signature}`
|
|
300
|
+
},
|
|
301
|
+
body: JSON.stringify({
|
|
302
|
+
ownerAddress: params.ownerAddress,
|
|
303
|
+
scope: params.scope,
|
|
304
|
+
expectedVersion: params.expectedVersion,
|
|
305
|
+
signature: params.signature
|
|
306
|
+
})
|
|
307
|
+
});
|
|
308
|
+
if (res.status === 404) {
|
|
309
|
+
throw new import_errors.DataPointNotFoundError(
|
|
310
|
+
`Data point ${dataPointId} (scope '${params.scope}') is not registered`,
|
|
311
|
+
details
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
if (res.status === 409) {
|
|
315
|
+
const body2 = await readBody(res);
|
|
316
|
+
const currentExpectedVersion = stringOrUndefined(
|
|
317
|
+
body2["currentExpectedVersion"]
|
|
318
|
+
);
|
|
319
|
+
const detail = stringOrUndefined(body2["error"]) ?? res.statusText;
|
|
320
|
+
throw new import_errors.DataPointVersionConflictError(
|
|
321
|
+
`Gateway error: 409 ${detail}`,
|
|
322
|
+
{
|
|
323
|
+
...details,
|
|
324
|
+
expectedVersion: params.expectedVersion,
|
|
325
|
+
currentExpectedVersion
|
|
326
|
+
}
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
if (res.status === 410) {
|
|
330
|
+
throw await deletedError(res, details);
|
|
331
|
+
}
|
|
332
|
+
if (!res.ok) {
|
|
333
|
+
const body2 = await readBody(res);
|
|
334
|
+
const detail = stringOrUndefined(body2["error"]) ?? res.statusText;
|
|
335
|
+
throw new Error(`Gateway error: ${res.status} ${detail}`);
|
|
336
|
+
}
|
|
337
|
+
const body = await readBody(res);
|
|
338
|
+
return {
|
|
339
|
+
dataPointId: getMutationId(body, "dataPointId") ?? dataPointId,
|
|
340
|
+
ownerAddress: stringOrUndefined(body["ownerAddress"]),
|
|
341
|
+
scope: stringOrUndefined(body["scope"]),
|
|
342
|
+
dataHash: stringOrUndefined(body["dataHash"]),
|
|
343
|
+
metadataHash: stringOrUndefined(body["metadataHash"]),
|
|
344
|
+
expectedVersion: stringOrUndefined(body["expectedVersion"]),
|
|
345
|
+
deletedAt: (0, import_data_point_deletion.tombstoneDeletedAt)(body)
|
|
229
346
|
};
|
|
230
347
|
},
|
|
231
348
|
async createGrant(params) {
|
|
@@ -244,18 +361,14 @@ function createGatewayClient(baseUrl) {
|
|
|
244
361
|
})
|
|
245
362
|
});
|
|
246
363
|
if (res.status === 409) {
|
|
247
|
-
const body2 = await
|
|
248
|
-
return {
|
|
249
|
-
grantId: getMutationId(body2, "grantId")
|
|
250
|
-
};
|
|
364
|
+
const body2 = await (0, import_response_body.readJsonObject)(res);
|
|
365
|
+
return { grantId: getMutationId(body2, "grantId") };
|
|
251
366
|
}
|
|
252
367
|
if (!res.ok) {
|
|
253
368
|
throw new Error(`Gateway error: ${res.status} ${res.statusText}`);
|
|
254
369
|
}
|
|
255
|
-
const body = await
|
|
256
|
-
return {
|
|
257
|
-
grantId: getMutationId(body, "grantId")
|
|
258
|
-
};
|
|
370
|
+
const body = await (0, import_response_body.readJsonObject)(res);
|
|
371
|
+
return { grantId: getMutationId(body, "grantId") };
|
|
259
372
|
},
|
|
260
373
|
async revokeGrant(params) {
|
|
261
374
|
const res = await fetch(`${base}/v1/grants/${params.grantId}`, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/protocol/gateway.ts"],"sourcesContent":["import { tryGrantPermissions, type GrantPermission } from \"./scope-actions\";\nexport interface GatewayEnvelope<T> {\n data: T;\n proof: GatewayProof;\n /**\n * Cursor-based pagination metadata, present on list endpoints (e.g.\n * `GET /v1/data`). A sibling of `data`, not nested inside it — so callers\n * that need it must read the full envelope rather than going through\n * `unwrapEnvelope`, which intentionally returns only `data`.\n */\n pagination?: GatewayPagination;\n}\n\nexport interface GatewayPagination {\n limit: number;\n hasMore: boolean;\n /**\n * Opaque cursor for the NEXT page; pass back as the `cursor` query param.\n * Null when there are no further pages.\n */\n nextCursor: string | null;\n}\n\nexport interface GatewayProof {\n signature: string;\n timestamp: string;\n gatewayAddress: string;\n requestHash: string;\n responseHash: string;\n userSignature: string;\n status: string;\n chainBlockHeight: number;\n}\n\nexport interface Builder {\n id: string;\n ownerAddress: string;\n granteeAddress: string;\n publicKey: string;\n appUrl: string;\n addedAt: string;\n}\n\nexport interface Schema {\n id: string;\n ownerAddress: string;\n name: string;\n definitionUrl: string;\n scope: string;\n addedAt: string;\n}\n\n// Row shape returned by `GET /v1/servers?owner=…`. A discovery-only list —\n// the endpoint carries no per-item gateway attestation (use\n// `getServer(serverAddress)` if you need a verifiable single-server proof).\n// `status` is the gateway's chain-sync state ('pending' | 'confirmed' |\n// 'finalized' | 'reorged'); kept as an open string since the gateway may add\n// states without a SDK bump.\nexport interface OwnerServerRecord {\n id: string;\n ownerAddress: string;\n serverAddress: string;\n publicKey: string;\n serverUrl: string;\n status: string;\n chainBlockHeight: string | null;\n // ISO timestamp of the gateway-side upsert.\n addedAt: string;\n // ISO timestamp when the grantor deregistered this server. Always null for\n // entries in the `active` list, always non-null for `revoked`.\n revokedAt: string | null;\n}\n\n// Response from `GET /v1/servers?owner=…`. Split into two arrays; each is\n// ordered newest-first (`addedAt DESC`, `id ASC` tiebreaker) so `active[0]`\n// is unambiguously the newest active server — the sensible default when the\n// caller just needs one URL. The gateway caps the total across both arrays\n// at 200 — per-owner counts are tiny in practice, so no cursor.\nexport interface OwnerServersResult {\n active: OwnerServerRecord[];\n revoked: OwnerServerRecord[];\n count: number;\n}\n\nexport interface ServerInfo {\n id: string;\n ownerAddress: string;\n serverAddress: string;\n publicKey: string;\n serverUrl: string;\n addedAt: string;\n // ISO timestamp when the grantor deregistered this server, null while\n // active. The gateway returns this on /v1/servers/:address GETs since\n // covering revocation in the response keeps the attestation hash\n // authoritative for both states.\n revokedAt: string | null;\n}\n\n// Fee annotation surfaced on every GET grant response. Amounts are decimal\n// uint256 strings to match `/v1/escrow/pay`'s wire format. totalDue is a\n// snapshot — the gateway re-resolves fees at pay time, so clients shouldn't\n// cache this across requests.\nexport interface GatewayGrantFee {\n asset: string;\n registrationFee: string;\n dataAccessFee: string;\n totalDue: string;\n}\n\n// Lifecycle of a grant's on-chain settlement, tracked separately from the\n// fee-payment lifecycle (paymentStatus). Driven by POST /v1/settle:\n// pending — nothing on-chain yet\n// submitting — settle tx broadcast but receipt not yet observed\n// confirmed — settle tx mined successfully\n// finalized — finalized tip past the settle block, reorg-safe\n// reorged — finalized observation reverted; back to 'pending' on next settle\nexport type GatewayGrantStatus =\n | \"pending\"\n | \"submitting\"\n | \"confirmed\"\n | \"finalized\"\n | \"reorged\";\n\nexport interface GatewayGrantResponse {\n id: string;\n grantorAddress: string;\n granteeId: string;\n // The signed scope entries, verbatim (`[operation:]scope`, bare = read).\n // This is the wire form: it is what the grantor signed and what the\n // gateway stores. Render `permissions` instead of parsing these.\n scopes: string[];\n // Derived by the SDK at read time from `scopes` via grantPermissions():\n // one `{ scope, actions }` row per scope pattern, canonically ordered.\n // Never sent to the gateway and never part of the signed payload. Absent\n // when `scopes` carries an entry this SDK version cannot interpret (an\n // operation introduced after this release) - fall back to `scopes` and\n // do not assume such a grant is read-only.\n permissions?: GrantPermission[];\n status: GatewayGrantStatus;\n addedAt: string;\n // Grantor-signed deadline. null = perpetual grant (signed value was 0).\n expiresAt: string | null;\n // Derived at read time from expiresAt vs the gateway's clock — a snapshot,\n // not a cached truth. Re-check against expiresAt locally if you care.\n expired: boolean;\n revokedAt: string | null;\n revocationSignature: string | null;\n // 'pending' until the grant registration fee is settled via /v1/escrow/pay.\n paymentStatus: \"pending\" | \"paid\";\n paidAt: string | null;\n paidBy: string | null;\n // Decimal-string uint256 monotonic nonce; advances on every state change.\n grantVersion: string;\n // Settle metadata — populated as the grant progresses through the chain\n // lifecycle. Null while `status === 'pending'`.\n settleTxHash: string | null;\n settleSubmittedAt: string | null;\n // Revocation metadata — populated independently when the grantor signs\n // and the gateway pushes a deregister tx.\n revocationTxHash: string | null;\n revocationSubmittedAt: string | null;\n fee: GatewayGrantFee;\n}\n\nexport type GrantListItem = GatewayGrantResponse;\n\n// Mirror of a DataRegistryV2 row as the gateway exposes it. `id` is the\n// deterministic `keccak256(abi.encode(owner, scope))` dataPointId — the same\n// value the contract uses as its primary key. `expectedVersion` is the latest\n// version the gateway has accepted; the on-chain row may be one version behind\n// while a settle is still pending. The gateway response intentionally drops\n// `status` from the body — read it from the on-chain contract when you need\n// the canonical lifecycle state.\nexport interface DataPointRecord {\n id: string;\n ownerAddress: string;\n scope: string;\n dataHash: string;\n metadataHash: string;\n // Decimal-string uint256.\n expectedVersion: string;\n // ISO 8601 timestamp of the most recent gateway-side upsert.\n addedAt: string;\n}\n\nexport interface DataPointListResult {\n dataPoints: DataPointRecord[];\n cursor: string | null;\n}\n\nexport interface ListDataPointsOptions {\n /**\n * Only return rows added at or after this ISO 8601 timestamp. Used by sync\n * loops that want incremental tails — pass the last seen `addedAt`.\n */\n since?: string;\n /** Page size. Capped at 1000 by the gateway. */\n limit?: number;\n}\n\n// grantVersion and expiresAt are decimal-string uint256s — same wire format\n// the gateway expects. The caller is responsible for converting their bigint\n// to a decimal string and for signing GRANT_REGISTRATION_TYPES with matching\n// bigint values.\nexport interface CreateGrantParams {\n grantorAddress: string;\n granteeId: string;\n scopes: string[];\n grantVersion: string;\n expiresAt: string;\n signature: string;\n}\n\nexport interface RevokeGrantParams {\n grantId: string;\n grantorAddress: string;\n grantVersion: string;\n signature: string;\n}\n\nexport interface RegisterServerParams {\n ownerAddress: string;\n serverAddress: string;\n publicKey: string;\n serverUrl: string;\n signature: string;\n}\n\nexport interface RegisterServerResult {\n serverId?: string;\n alreadyRegistered: boolean;\n}\n\nexport interface RegisterBuilderParams {\n ownerAddress: string;\n // Wallet the builder authenticates to the Personal Server with. The\n // builderId is deterministically derived from (owner, grantee, publicKey,\n // appUrl) so this triple pins the on-chain identity.\n granteeAddress: string;\n publicKey: string;\n appUrl: string;\n signature: string;\n}\n\nexport interface RegisterBuilderResult {\n builderId?: string;\n alreadyRegistered: boolean;\n}\n\n// AddData on DataRegistryV2. dataHash + metadataHash are bytes32 commitments\n// to the off-chain payload + its metadata. expectedVersion is a CAS knob —\n// the gateway/contract rejects on 409 if a higher version is already stored,\n// and the error body surfaces `currentExpectedVersion` so callers can re-sign.\nexport interface RegisterDataPointParams {\n ownerAddress: string;\n scope: string;\n dataHash: string;\n metadataHash: string;\n expectedVersion: string;\n signature: string;\n}\n\nexport interface RegisterDataPointResult {\n dataPointId?: string;\n expectedVersion?: string;\n}\n\n// ── Escrow / data-access payment path ───────────────────────────────────────\n// /v1/escrow/pay debits the payer's escrow balance for a payable op. For a\n// Legacy grants use opType='grant' and opId=grantId. Standalone receipt-bound\n// reads use opType='data_access' and opId=accessRecord.recordId. amount and\n// paymentNonce are decimal uint256 strings on the wire. The signature is the\n// raw EIP-712 hex of GENERIC_PAYMENT_TYPES against escrowPaymentDomain.\n\n// A server-signed delivery receipt attached to a data-access payment. The\n// signature is over RECORD_DATA_ACCESS_TYPES against dataRegistryDomain; the\n// signer must be a personal server the data point's owner has registered as\n// trusted. Shape validation in clients does not verify this signature; the\n// gateway verifies it before re-using it on-chain in the next /v1/settle pass\n// via DataRegistryV2.recordDataAccess, where `recordId` dedupes via\n// `_usedRecordIds`.\nexport interface AccessRecord {\n dataPointId: string;\n // Decimal-string uint256 — the data point version being attested to.\n version: string;\n // Must equal the enclosing payment's payerAddress (the gateway enforces).\n accessor: string;\n recordId: string;\n signature: string;\n}\n\nexport interface PayForOperationParams {\n payerAddress: string;\n opType: string;\n opId: string;\n asset: string;\n amount: string;\n // Per-payer monotonic; (payer, nonce, kind) must be unique. The gateway\n // returns 409 if reused — bump and re-sign.\n paymentNonce: string;\n signature: string;\n // Optional: attach a server-signed access record so the next /v1/settle\n // pass submits a recordDataAccess tx alongside the payment settlement.\n // Required for data-access payments (the second-and-onward payments per\n // grant) that want their on-chain `totalAccesses` counter to advance.\n accessRecord?: AccessRecord;\n}\n\nexport interface PayForOperationResult {\n opType: string;\n opId: string;\n payerAddress: string;\n asset: string;\n amount: string;\n // Echoes how the gateway split this payment. `registrationPaid` is true on\n // the first payment for a grant (which bundles both fees) and false on\n // subsequent data-access-only payments. Off-chain ledger state only — the\n // on-chain settlement of the registration is tracked by the grant's\n // `status` field, not this flag.\n breakdown: {\n registrationFee: string;\n dataAccessFee: string;\n registrationPaid: boolean;\n };\n paymentNonce: string;\n paidAt: string;\n}\n\n// ── Settle / reconcile ──────────────────────────────────────────────────────\n// POST /v1/settle drains pending-on-chain rows (grants, servers, builders,\n// data points, data-point statuses, access records) to the relayer, then\n// promotes 'submitting' → 'confirmed' and 'confirmed' → 'finalized' for\n// previously-submitted rows. One call does all three; the response surfaces\n// each phase's outcomes.\n\n// The op-types the settle endpoint emits in `items[]`. Kept as a union so\n// callers can narrow inside the discriminated SettleItem shape. Mirrors the\n// gateway's drain phases (drainGrants/drainServers/drainBuilders/\n// drainDataPoints/drainDataPointStatuses/drainAccessRecords) — keep in sync\n// when the gateway adds a drain.\nexport type SettleOpType =\n | \"grant\"\n | \"server\"\n | \"data\"\n | \"access\"\n | \"builder\"\n | \"data-status\";\n\nexport type SettleItem =\n | {\n opType: SettleOpType;\n opId: string;\n // 'confirmed' when the submit function waited for the receipt and it\n // mined (registerAndSettle path); 'submitting' when only the tx was\n // sent (no receipt wait).\n status: \"submitting\" | \"confirmed\";\n settleTxHash: string | null;\n settleSubmittedAt: string | null;\n // Block height the tx mined in; only set when status === 'confirmed'.\n chainBlockHeight: string | null;\n revocationTxHash: string | null;\n revocationSubmittedAt: string | null;\n // True while lib/settle.ts is in placeholder mode for this row's pass.\n placeholder: boolean;\n }\n | {\n opType: SettleOpType;\n opId: string;\n status: \"skipped\";\n reason: string;\n }\n | {\n opType: SettleOpType;\n opId: string;\n status: \"failed\";\n error: string;\n };\n\n// Outcome of the housekeeping pass that retries earlier `submitting` rows\n// whose receipt arrived after the prior /v1/settle's wait budget elapsed.\nexport interface SettlePromoteResult {\n opType: SettleOpType;\n opId: string;\n status: \"confirmed\" | \"failed\" | \"pending\" | \"skipped\";\n txHash: string;\n chainBlockHeight: string | null;\n reason?: string;\n}\n\n// Outcome of the reconcile pass that advances 'confirmed' → 'finalized' once\n// the chain's finalized tip catches up past the tx's block (or reverts to\n// 'pending' on reorg detection).\nexport interface SettleReconcileItem {\n opId: string;\n status: \"finalized\" | \"reorged\" | \"unchanged\";\n chainBlockHeight: string | null;\n settleTxHash: string | null;\n reason?: string;\n}\n\nexport interface SettleParams {\n // Per-phase cap. Bounded by MAX_LIMIT on the gateway side; omit to use\n // the gateway's default BATCH_LIMIT.\n limit?: number;\n}\n\nexport interface SettleResult {\n scanned: number;\n submitted: number;\n confirmed: number;\n skipped: number;\n failed: number;\n items: SettleItem[];\n promoted: { count: number; items: SettlePromoteResult[] };\n reconciled: {\n scanned: number;\n finalized: number;\n reorged: number;\n unchanged: number;\n items: SettleReconcileItem[];\n };\n // Present only when the gateway is configured for paced submission —\n // spreads work across several blocks within one /v1/settle invocation.\n paced?: { iterations: number };\n}\n\n// /v1/escrow/balance?account=... — pure read. Returns finalized balances by\n// asset, plus the lifecycle breakdown of deposits.\nexport interface EscrowBalanceEntry {\n asset: string;\n // Gross credited deposits for (account, asset). Decremented only when the\n // reconcile pass marks a payment finalized — NOT on /v1/escrow/pay.\n balance: string;\n // Sum of claimedAmount for deposits still in 'submitted' status — surfaced\n // separately so clients don't conflate \"credited\" with \"deposit announced\n // but not yet confirmed.\"\n pendingAmount: string;\n // Sum of payments.amount for (account, asset) regardless of settled status —\n // mirrors the /v1/escrow/pay handler's soft-lock counter. Subtract from\n // `balance` to see how much the payer can still authorise.\n authorizedAmount: string;\n // `max(balance − authorizedAmount, 0)`. The headroom a payer has against\n // the soft-lock before /v1/escrow/pay starts returning 402.\n availableAmount: string;\n updatedAt: string | null;\n}\n\nexport interface EscrowDepositSubmitted {\n txHash: string;\n submittedAt: string;\n claimedAsset: string;\n claimedAmount: string;\n}\n\nexport interface EscrowDepositFinalized {\n txHash: string;\n finalizedAt: string | null;\n blockNumber: string | null;\n claimedAsset: string;\n claimedAmount: string;\n}\n\nexport interface EscrowDepositFailed {\n txHash: string;\n submittedAt: string;\n claimedAsset: string;\n claimedAmount: string;\n lastError: string | null;\n}\n\nexport interface EscrowBalance {\n account: string;\n balances: EscrowBalanceEntry[];\n deposits: {\n submitted: EscrowDepositSubmitted[];\n finalized: EscrowDepositFinalized[];\n failed: EscrowDepositFailed[];\n };\n}\n\n// /v1/escrow/deposit announces an on-chain deposit tx so the gateway can\n// reconcile it into the payer's balance. The gateway extracts the credited\n// account from calldata — no off-chain claim about who paid.\nexport interface SubmitDepositParams {\n txHash: string;\n}\n\nexport interface DepositState {\n txHash: string;\n account: string;\n // 'submitted' | 'finalized' | 'failed' — kept open since the gateway adds\n // states (e.g. 'orphaned') as the deposit flow evolves.\n status: string;\n blockNumber: string | null;\n submittedAt: string;\n finalizedAt: string | null;\n lastError: string | null;\n}\n\nexport interface GatewayClient {\n isRegisteredBuilder(address: string): Promise<boolean>;\n getBuilder(address: string): Promise<Builder | null>;\n getGrant(grantId: string): Promise<GatewayGrantResponse | null>;\n listGrantsByUser(userAddress: string): Promise<GrantListItem[]>;\n getSchemaForScope(scope: string): Promise<Schema | null>;\n getServer(address: string): Promise<ServerInfo | null>;\n /**\n * List every personal server an owner has registered — split into `active`\n * (currently trusted) and `revoked` (deregistered). Each list is ordered\n * newest-first; `active[0]` is the sensible default when the caller just\n * needs one URL for the owner. Empty owner returns `{active: [], revoked: [], count: 0}`.\n * Discovery-only endpoint — no per-server attestation; use `getServer` for that.\n */\n listServersByOwner(owner: string): Promise<OwnerServersResult>;\n /**\n * Fetch a single data point by its deterministic id (keccak256 of (owner, scope)).\n * Returns null on 404. The gateway omits `status` from the response body — read it\n * from the on-chain DataRegistryV2 contract when you need the canonical lifecycle state.\n */\n getDataPoint(dataPointId: string): Promise<DataPointRecord | null>;\n /**\n * Page through an owner's data points. Cursor is opaque; pass `null` for the first\n * page and feed back `result.cursor` until it returns null.\n */\n listDataPointsByOwner(\n owner: string,\n cursor: string | null,\n options?: ListDataPointsOptions,\n ): Promise<DataPointListResult>;\n getSchema(schemaId: string): Promise<Schema | null>;\n registerServer(params: RegisterServerParams): Promise<RegisterServerResult>;\n registerBuilder(\n params: RegisterBuilderParams,\n ): Promise<RegisterBuilderResult>;\n registerDataPoint(\n params: RegisterDataPointParams,\n ): Promise<RegisterDataPointResult>;\n createGrant(params: CreateGrantParams): Promise<{ grantId?: string }>;\n revokeGrant(params: RevokeGrantParams): Promise<void>;\n getEscrowBalance(account: string): Promise<EscrowBalance>;\n submitEscrowDeposit(params: SubmitDepositParams): Promise<DepositState>;\n payForOperation(\n params: PayForOperationParams,\n ): Promise<PayForOperationResult>;\n settle(params?: SettleParams): Promise<SettleResult>;\n}\n\n// Attach the derived `permissions` view to a grant record read back from the\n// gateway. The wire `scopes` are left untouched.\nfunction withGrantPermissions<T extends GatewayGrantResponse>(grant: T): T {\n // Gateway responses are untrusted runtime data despite their type. The\n // `permissions` view is SDK-owned: drop whatever the body carried under\n // that name, then derive only from a well-formed string[] and leave the\n // field absent otherwise, so a spoofed or stale value can never be\n // rendered as this grant's authority.\n const stripped = { ...grant };\n delete stripped.permissions;\n const scopes: unknown = stripped.scopes;\n if (!Array.isArray(scopes)) {\n return stripped;\n }\n const permissions = tryGrantPermissions(scopes as string[]);\n return permissions === undefined ? stripped : { ...stripped, permissions };\n}\n\nexport function createGatewayClient(baseUrl: string): GatewayClient {\n const base = baseUrl.replace(/\\/+$/, \"\");\n\n async function unwrapEnvelope<T>(res: Response): Promise<T> {\n const envelope = (await res.json()) as GatewayEnvelope<T>;\n return envelope.data;\n }\n\n function getMutationId(\n body: Record<string, unknown>,\n key: string,\n ): string | undefined {\n const value = body[key] ?? body[\"id\"];\n return typeof value === \"string\" ? value : undefined;\n }\n\n return {\n async isRegisteredBuilder(address: string): Promise<boolean> {\n const builder = await this.getBuilder(address);\n return builder !== null;\n },\n\n async getBuilder(address: string): Promise<Builder | null> {\n const res = await fetch(`${base}/v1/builders/${address}`);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return unwrapEnvelope<Builder>(res);\n },\n\n async getGrant(grantId: string): Promise<GatewayGrantResponse | null> {\n const res = await fetch(`${base}/v1/grants/${grantId}`);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return withGrantPermissions(\n await unwrapEnvelope<GatewayGrantResponse>(res),\n );\n },\n\n async listGrantsByUser(userAddress: string): Promise<GrantListItem[]> {\n const res = await fetch(`${base}/v1/grants?user=${userAddress}`);\n if (res.status === 404) return [];\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n const grants = await unwrapEnvelope<GrantListItem[]>(res);\n return grants.map(withGrantPermissions);\n },\n\n async getSchemaForScope(scope: string): Promise<Schema | null> {\n const res = await fetch(`${base}/v1/schemas?scope=${scope}`);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return unwrapEnvelope<Schema>(res);\n },\n\n async getServer(address: string): Promise<ServerInfo | null> {\n const res = await fetch(`${base}/v1/servers/${address}`);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return unwrapEnvelope<ServerInfo>(res);\n },\n\n async listServersByOwner(owner: string): Promise<OwnerServersResult> {\n // URLSearchParams encodes the owner so a malformed value (e.g.\n // \"0xabc&foo=bar\") can't inject extra query params. Same pattern as\n // listDataPointsByOwner.\n const params = new URLSearchParams({ owner });\n const res = await fetch(`${base}/v1/servers?${params.toString()}`);\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n // Unlike /v1/servers/:address, the list endpoint returns the body\n // directly — no GatewayEnvelope, no attestation (discovery-only).\n return (await res.json()) as OwnerServersResult;\n },\n\n async getDataPoint(dataPointId: string): Promise<DataPointRecord | null> {\n const res = await fetch(`${base}/v1/data/${dataPointId}`);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return unwrapEnvelope<DataPointRecord>(res);\n },\n\n async listDataPointsByOwner(\n owner: string,\n cursor: string | null,\n options?: ListDataPointsOptions,\n ): Promise<DataPointListResult> {\n const params = new URLSearchParams({ user: owner });\n if (cursor !== null) {\n params.set(\"cursor\", cursor);\n }\n if (options?.since) {\n params.set(\"since\", options.since);\n }\n if (options?.limit !== undefined) {\n params.set(\"limit\", String(options.limit));\n }\n const res = await fetch(`${base}/v1/data?${params.toString()}`);\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n // Next-page cursor lives in the envelope's `pagination.nextCursor`\n // (a sibling of `data`), so read the full envelope rather than going\n // through `unwrapEnvelope`, which returns only `data`.\n const envelope = (await res.json()) as GatewayEnvelope<{\n dataPoints: DataPointRecord[];\n }>;\n const nextCursor =\n envelope.pagination?.hasMore === false\n ? null\n : (envelope.pagination?.nextCursor ?? null);\n return {\n dataPoints: envelope.data.dataPoints,\n cursor: nextCursor,\n };\n },\n\n async getSchema(schemaId: string): Promise<Schema | null> {\n const res = await fetch(`${base}/v1/schemas/${schemaId}`);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return unwrapEnvelope<Schema>(res);\n },\n\n async registerServer(\n params: RegisterServerParams,\n ): Promise<RegisterServerResult> {\n const res = await fetch(`${base}/v1/servers`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${params.signature}`,\n },\n body: JSON.stringify({\n ownerAddress: params.ownerAddress,\n serverAddress: params.serverAddress,\n publicKey: params.publicKey,\n serverUrl: params.serverUrl,\n }),\n });\n if (res.status === 409) {\n const body = await res.json().catch(() => ({}));\n return {\n serverId: getMutationId(body as Record<string, unknown>, \"serverId\"),\n alreadyRegistered: true,\n };\n }\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n const body = await res.json().catch(() => ({}));\n return {\n serverId: getMutationId(body as Record<string, unknown>, \"serverId\"),\n alreadyRegistered: false,\n };\n },\n\n async registerBuilder(\n params: RegisterBuilderParams,\n ): Promise<RegisterBuilderResult> {\n const res = await fetch(`${base}/v1/builders`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${params.signature}`,\n },\n body: JSON.stringify({\n ownerAddress: params.ownerAddress,\n granteeAddress: params.granteeAddress,\n publicKey: params.publicKey,\n appUrl: params.appUrl,\n }),\n });\n // 409 is idempotent — the gateway's current 409 body doesn't include\n // the builderId, but we tolerate it in case that changes (mirrors the\n // registerServer / createGrant shape).\n if (res.status === 409) {\n const body = await res.json().catch(() => ({}));\n return {\n builderId: getMutationId(\n body as Record<string, unknown>,\n \"builderId\",\n ),\n alreadyRegistered: true,\n };\n }\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n const body = await res.json().catch(() => ({}));\n return {\n builderId: getMutationId(body as Record<string, unknown>, \"builderId\"),\n alreadyRegistered: false,\n };\n },\n\n async registerDataPoint(\n params: RegisterDataPointParams,\n ): Promise<RegisterDataPointResult> {\n const res = await fetch(`${base}/v1/data`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${params.signature}`,\n },\n body: JSON.stringify({\n ownerAddress: params.ownerAddress,\n scope: params.scope,\n dataHash: params.dataHash,\n metadataHash: params.metadataHash,\n expectedVersion: params.expectedVersion,\n }),\n });\n // 409 is a real failure here (stale CAS), not an idempotent replay —\n // surface the gateway's error message verbatim so the caller knows\n // what `currentExpectedVersion` to re-sign against.\n if (!res.ok) {\n const body = (await res.json().catch(() => ({}))) as {\n error?: string;\n };\n const detail = body.error ?? res.statusText;\n throw new Error(`Gateway error: ${res.status} ${detail}`);\n }\n const body = (await res.json().catch(() => ({}))) as {\n dataPointId?: string;\n expectedVersion?: string;\n };\n return {\n dataPointId: getMutationId(\n body as Record<string, unknown>,\n \"dataPointId\",\n ),\n expectedVersion: body.expectedVersion,\n };\n },\n\n async createGrant(\n params: CreateGrantParams,\n ): Promise<{ grantId?: string }> {\n const res = await fetch(`${base}/v1/grants`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${params.signature}`,\n },\n body: JSON.stringify({\n grantorAddress: params.grantorAddress,\n granteeId: params.granteeId,\n scopes: params.scopes,\n grantVersion: params.grantVersion,\n expiresAt: params.expiresAt,\n }),\n });\n if (res.status === 409) {\n const body = await res.json().catch(() => ({}));\n return {\n grantId: getMutationId(body as Record<string, unknown>, \"grantId\"),\n };\n }\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n const body = await res.json();\n return {\n grantId: getMutationId(body as Record<string, unknown>, \"grantId\"),\n };\n },\n\n async revokeGrant(params: RevokeGrantParams): Promise<void> {\n const res = await fetch(`${base}/v1/grants/${params.grantId}`, {\n method: \"DELETE\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${params.signature}`,\n },\n body: JSON.stringify({\n grantorAddress: params.grantorAddress,\n grantVersion: params.grantVersion,\n }),\n });\n if (res.status === 409) return;\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n },\n\n async getEscrowBalance(account: string): Promise<EscrowBalance> {\n const res = await fetch(`${base}/v1/escrow/balance?account=${account}`);\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n // Unlike the rest of /v1, the balance endpoint returns the body\n // directly (no GatewayEnvelope wrap) — it's a pure read with no\n // gateway-signed attestation. See data-gateway api/v1/escrow/balance.ts.\n return (await res.json()) as EscrowBalance;\n },\n\n async submitEscrowDeposit(\n params: SubmitDepositParams,\n ): Promise<DepositState> {\n const res = await fetch(`${base}/v1/escrow/deposit`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ txHash: params.txHash }),\n });\n // The gateway returns 202 for \"accepted (still confirming)\" and 200 for\n // duplicate idempotent replays. Both carry the deposit's current state.\n if (res.status !== 200 && res.status !== 202) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as DepositState;\n },\n\n async payForOperation(\n params: PayForOperationParams,\n ): Promise<PayForOperationResult> {\n // Build the body without the accessRecord key when absent so the\n // gateway's \"missing optional\" branch matches the no-receipt case\n // exactly (an explicit `accessRecord: undefined` would JSON-serialize\n // to nothing — same result — but keeping it conditional makes wire\n // traces easier to read).\n const body: Record<string, unknown> = {\n payerAddress: params.payerAddress,\n opType: params.opType,\n opId: params.opId,\n asset: params.asset,\n amount: params.amount,\n paymentNonce: params.paymentNonce,\n };\n if (params.accessRecord) {\n body[\"accessRecord\"] = params.accessRecord;\n }\n const res = await fetch(`${base}/v1/escrow/pay`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${params.signature}`,\n },\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as PayForOperationResult;\n },\n\n async settle(params?: SettleParams): Promise<SettleResult> {\n const res = await fetch(`${base}/v1/settle`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n // The gateway accepts an empty body; only `limit` is recognised.\n // Always send a JSON body so the gateway's req.body shape parse\n // doesn't have to deal with an undefined.\n body: JSON.stringify(params ?? {}),\n });\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as SettleResult;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAA0D;AAqiB1D,SAAS,qBAAqD,OAAa;AAMzE,QAAM,WAAW,EAAE,GAAG,MAAM;AAC5B,SAAO,SAAS;AAChB,QAAM,SAAkB,SAAS;AACjC,MAAI,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC1B,WAAO;AAAA,EACT;AACA,QAAM,kBAAc,0CAAoB,MAAkB;AAC1D,SAAO,gBAAgB,SAAY,WAAW,EAAE,GAAG,UAAU,YAAY;AAC3E;AAEO,SAAS,oBAAoB,SAAgC;AAClE,QAAM,OAAO,QAAQ,QAAQ,QAAQ,EAAE;AAEvC,iBAAe,eAAkB,KAA2B;AAC1D,UAAM,WAAY,MAAM,IAAI,KAAK;AACjC,WAAO,SAAS;AAAA,EAClB;AAEA,WAAS,cACP,MACA,KACoB;AACpB,UAAM,QAAQ,KAAK,GAAG,KAAK,KAAK,IAAI;AACpC,WAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,EAC7C;AAEA,SAAO;AAAA,IACL,MAAM,oBAAoB,SAAmC;AAC3D,YAAM,UAAU,MAAM,KAAK,WAAW,OAAO;AAC7C,aAAO,YAAY;AAAA,IACrB;AAAA,IAEA,MAAM,WAAW,SAA0C;AACzD,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,gBAAgB,OAAO,EAAE;AACxD,UAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AACA,aAAO,eAAwB,GAAG;AAAA,IACpC;AAAA,IAEA,MAAM,SAAS,SAAuD;AACpE,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,cAAc,OAAO,EAAE;AACtD,UAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AACA,aAAO;AAAA,QACL,MAAM,eAAqC,GAAG;AAAA,MAChD;AAAA,IACF;AAAA,IAEA,MAAM,iBAAiB,aAA+C;AACpE,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,mBAAmB,WAAW,EAAE;AAC/D,UAAI,IAAI,WAAW,IAAK,QAAO,CAAC;AAChC,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AACA,YAAM,SAAS,MAAM,eAAgC,GAAG;AACxD,aAAO,OAAO,IAAI,oBAAoB;AAAA,IACxC;AAAA,IAEA,MAAM,kBAAkB,OAAuC;AAC7D,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,qBAAqB,KAAK,EAAE;AAC3D,UAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AACA,aAAO,eAAuB,GAAG;AAAA,IACnC;AAAA,IAEA,MAAM,UAAU,SAA6C;AAC3D,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,eAAe,OAAO,EAAE;AACvD,UAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AACA,aAAO,eAA2B,GAAG;AAAA,IACvC;AAAA,IAEA,MAAM,mBAAmB,OAA4C;AAInE,YAAM,SAAS,IAAI,gBAAgB,EAAE,MAAM,CAAC;AAC5C,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,eAAe,OAAO,SAAS,CAAC,EAAE;AACjE,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AAGA,aAAQ,MAAM,IAAI,KAAK;AAAA,IACzB;AAAA,IAEA,MAAM,aAAa,aAAsD;AACvE,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,YAAY,WAAW,EAAE;AACxD,UAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AACA,aAAO,eAAgC,GAAG;AAAA,IAC5C;AAAA,IAEA,MAAM,sBACJ,OACA,QACA,SAC8B;AAC9B,YAAM,SAAS,IAAI,gBAAgB,EAAE,MAAM,MAAM,CAAC;AAClD,UAAI,WAAW,MAAM;AACnB,eAAO,IAAI,UAAU,MAAM;AAAA,MAC7B;AACA,UAAI,SAAS,OAAO;AAClB,eAAO,IAAI,SAAS,QAAQ,KAAK;AAAA,MACnC;AACA,UAAI,SAAS,UAAU,QAAW;AAChC,eAAO,IAAI,SAAS,OAAO,QAAQ,KAAK,CAAC;AAAA,MAC3C;AACA,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,YAAY,OAAO,SAAS,CAAC,EAAE;AAC9D,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AAIA,YAAM,WAAY,MAAM,IAAI,KAAK;AAGjC,YAAM,aACJ,SAAS,YAAY,YAAY,QAC7B,OACC,SAAS,YAAY,cAAc;AAC1C,aAAO;AAAA,QACL,YAAY,SAAS,KAAK;AAAA,QAC1B,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,IAEA,MAAM,UAAU,UAA0C;AACxD,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,eAAe,QAAQ,EAAE;AACxD,UAAI,IAAI,WAAW,IAAK,QAAO;AAC/B,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AACA,aAAO,eAAuB,GAAG;AAAA,IACnC;AAAA,IAEA,MAAM,eACJ,QAC+B;AAC/B,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,eAAe;AAAA,QAC5C,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,cAAc,OAAO,SAAS;AAAA,QAC/C;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,cAAc,OAAO;AAAA,UACrB,eAAe,OAAO;AAAA,UACtB,WAAW,OAAO;AAAA,UAClB,WAAW,OAAO;AAAA,QACpB,CAAC;AAAA,MACH,CAAC;AACD,UAAI,IAAI,WAAW,KAAK;AACtB,cAAMA,QAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,eAAO;AAAA,UACL,UAAU,cAAcA,OAAiC,UAAU;AAAA,UACnE,mBAAmB;AAAA,QACrB;AAAA,MACF;AACA,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AACA,YAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,aAAO;AAAA,QACL,UAAU,cAAc,MAAiC,UAAU;AAAA,QACnE,mBAAmB;AAAA,MACrB;AAAA,IACF;AAAA,IAEA,MAAM,gBACJ,QACgC;AAChC,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,gBAAgB;AAAA,QAC7C,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,cAAc,OAAO,SAAS;AAAA,QAC/C;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,cAAc,OAAO;AAAA,UACrB,gBAAgB,OAAO;AAAA,UACvB,WAAW,OAAO;AAAA,UAClB,QAAQ,OAAO;AAAA,QACjB,CAAC;AAAA,MACH,CAAC;AAID,UAAI,IAAI,WAAW,KAAK;AACtB,cAAMA,QAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,eAAO;AAAA,UACL,WAAW;AAAA,YACTA;AAAA,YACA;AAAA,UACF;AAAA,UACA,mBAAmB;AAAA,QACrB;AAAA,MACF;AACA,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AACA,YAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,aAAO;AAAA,QACL,WAAW,cAAc,MAAiC,WAAW;AAAA,QACrE,mBAAmB;AAAA,MACrB;AAAA,IACF;AAAA,IAEA,MAAM,kBACJ,QACkC;AAClC,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,YAAY;AAAA,QACzC,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,cAAc,OAAO,SAAS;AAAA,QAC/C;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,cAAc,OAAO;AAAA,UACrB,OAAO,OAAO;AAAA,UACd,UAAU,OAAO;AAAA,UACjB,cAAc,OAAO;AAAA,UACrB,iBAAiB,OAAO;AAAA,QAC1B,CAAC;AAAA,MACH,CAAC;AAID,UAAI,CAAC,IAAI,IAAI;AACX,cAAMA,QAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAG/C,cAAM,SAASA,MAAK,SAAS,IAAI;AACjC,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,MAAM,EAAE;AAAA,MAC1D;AACA,YAAM,OAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAI/C,aAAO;AAAA,QACL,aAAa;AAAA,UACX;AAAA,UACA;AAAA,QACF;AAAA,QACA,iBAAiB,KAAK;AAAA,MACxB;AAAA,IACF;AAAA,IAEA,MAAM,YACJ,QAC+B;AAC/B,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,cAAc;AAAA,QAC3C,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,cAAc,OAAO,SAAS;AAAA,QAC/C;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,gBAAgB,OAAO;AAAA,UACvB,WAAW,OAAO;AAAA,UAClB,QAAQ,OAAO;AAAA,UACf,cAAc,OAAO;AAAA,UACrB,WAAW,OAAO;AAAA,QACpB,CAAC;AAAA,MACH,CAAC;AACD,UAAI,IAAI,WAAW,KAAK;AACtB,cAAMA,QAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,eAAO;AAAA,UACL,SAAS,cAAcA,OAAiC,SAAS;AAAA,QACnE;AAAA,MACF;AACA,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AACA,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,aAAO;AAAA,QACL,SAAS,cAAc,MAAiC,SAAS;AAAA,MACnE;AAAA,IACF;AAAA,IAEA,MAAM,YAAY,QAA0C;AAC1D,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,cAAc,OAAO,OAAO,IAAI;AAAA,QAC7D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,cAAc,OAAO,SAAS;AAAA,QAC/C;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,gBAAgB,OAAO;AAAA,UACvB,cAAc,OAAO;AAAA,QACvB,CAAC;AAAA,MACH,CAAC;AACD,UAAI,IAAI,WAAW,IAAK;AACxB,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AAAA,IACF;AAAA,IAEA,MAAM,iBAAiB,SAAyC;AAC9D,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,8BAA8B,OAAO,EAAE;AACtE,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AAIA,aAAQ,MAAM,IAAI,KAAK;AAAA,IACzB;AAAA,IAEA,MAAM,oBACJ,QACuB;AACvB,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,sBAAsB;AAAA,QACnD,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,QAAQ,OAAO,OAAO,CAAC;AAAA,MAChD,CAAC;AAGD,UAAI,IAAI,WAAW,OAAO,IAAI,WAAW,KAAK;AAC5C,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AACA,aAAQ,MAAM,IAAI,KAAK;AAAA,IACzB;AAAA,IAEA,MAAM,gBACJ,QACgC;AAMhC,YAAM,OAAgC;AAAA,QACpC,cAAc,OAAO;AAAA,QACrB,QAAQ,OAAO;AAAA,QACf,MAAM,OAAO;AAAA,QACb,OAAO,OAAO;AAAA,QACd,QAAQ,OAAO;AAAA,QACf,cAAc,OAAO;AAAA,MACvB;AACA,UAAI,OAAO,cAAc;AACvB,aAAK,cAAc,IAAI,OAAO;AAAA,MAChC;AACA,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,kBAAkB;AAAA,QAC/C,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,cAAc,OAAO,SAAS;AAAA,QAC/C;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,MAC3B,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AACA,aAAQ,MAAM,IAAI,KAAK;AAAA,IACzB;AAAA,IAEA,MAAM,OAAO,QAA8C;AACzD,YAAM,MAAM,MAAM,MAAM,GAAG,IAAI,cAAc;AAAA,QAC3C,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA;AAAA;AAAA;AAAA,QAI9C,MAAM,KAAK,UAAU,UAAU,CAAC,CAAC;AAAA,MACnC,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,IAAI,MAAM,kBAAkB,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,MAClE;AACA,aAAQ,MAAM,IAAI,KAAK;AAAA,IACzB;AAAA,EACF;AACF;","names":["body"]}
|
|
1
|
+
{"version":3,"sources":["../../src/protocol/gateway.ts"],"sourcesContent":["import { tryGrantPermissions, type GrantPermission } from \"./scope-actions\";\nimport {\n DataPointDeletedError,\n DataPointNotFoundError,\n DataPointVersionConflictError,\n} from \"../errors\";\nimport {\n isDataPointTombstone,\n tombstoneDeletedAt,\n} from \"./data-point-deletion\";\nimport { deriveDataPointId } from \"./lineage\";\nimport {\n isPlainObject,\n readJsonObject,\n readJsonValue,\n} from \"../utils/response-body\";\n\nexport interface GatewayEnvelope<T> {\n data: T;\n proof: GatewayProof;\n /**\n * Cursor-based pagination metadata, present on list endpoints (e.g.\n * `GET /v1/data`). A sibling of `data`, not nested inside it — so callers\n * that need it must read the full envelope rather than going through\n * `unwrapEnvelope`, which intentionally returns only `data`.\n */\n pagination?: GatewayPagination;\n}\n\nexport interface GatewayPagination {\n limit: number;\n hasMore: boolean;\n /**\n * Opaque cursor for the NEXT page; pass back as the `cursor` query param.\n * Null when there are no further pages.\n */\n nextCursor: string | null;\n}\n\nexport interface GatewayProof {\n signature: string;\n timestamp: string;\n gatewayAddress: string;\n requestHash: string;\n responseHash: string;\n userSignature: string;\n status: string;\n chainBlockHeight: number;\n}\n\nexport interface Builder {\n id: string;\n ownerAddress: string;\n granteeAddress: string;\n publicKey: string;\n appUrl: string;\n addedAt: string;\n}\n\nexport interface Schema {\n id: string;\n ownerAddress: string;\n name: string;\n definitionUrl: string;\n scope: string;\n addedAt: string;\n}\n\n// Row shape returned by `GET /v1/servers?owner=…`. A discovery-only list —\n// the endpoint carries no per-item gateway attestation (use\n// `getServer(serverAddress)` if you need a verifiable single-server proof).\n// `status` is the gateway's chain-sync state ('pending' | 'confirmed' |\n// 'finalized' | 'reorged'); kept as an open string since the gateway may add\n// states without a SDK bump.\nexport interface OwnerServerRecord {\n id: string;\n ownerAddress: string;\n serverAddress: string;\n publicKey: string;\n serverUrl: string;\n status: string;\n chainBlockHeight: string | null;\n // ISO timestamp of the gateway-side upsert.\n addedAt: string;\n // ISO timestamp when the grantor deregistered this server. Always null for\n // entries in the `active` list, always non-null for `revoked`.\n revokedAt: string | null;\n}\n\n// Response from `GET /v1/servers?owner=…`. Split into two arrays; each is\n// ordered newest-first (`addedAt DESC`, `id ASC` tiebreaker) so `active[0]`\n// is unambiguously the newest active server — the sensible default when the\n// caller just needs one URL. The gateway caps the total across both arrays\n// at 200 — per-owner counts are tiny in practice, so no cursor.\nexport interface OwnerServersResult {\n active: OwnerServerRecord[];\n revoked: OwnerServerRecord[];\n count: number;\n}\n\nexport interface ServerInfo {\n id: string;\n ownerAddress: string;\n serverAddress: string;\n publicKey: string;\n serverUrl: string;\n addedAt: string;\n // ISO timestamp when the grantor deregistered this server, null while\n // active. The gateway returns this on /v1/servers/:address GETs since\n // covering revocation in the response keeps the attestation hash\n // authoritative for both states.\n revokedAt: string | null;\n}\n\n// Fee annotation surfaced on every GET grant response. Amounts are decimal\n// uint256 strings to match `/v1/escrow/pay`'s wire format. totalDue is a\n// snapshot — the gateway re-resolves fees at pay time, so clients shouldn't\n// cache this across requests.\nexport interface GatewayGrantFee {\n asset: string;\n registrationFee: string;\n dataAccessFee: string;\n totalDue: string;\n}\n\n// Lifecycle of a grant's on-chain settlement, tracked separately from the\n// fee-payment lifecycle (paymentStatus). Driven by POST /v1/settle:\n// pending — nothing on-chain yet\n// submitting — settle tx broadcast but receipt not yet observed\n// confirmed — settle tx mined successfully\n// finalized — finalized tip past the settle block, reorg-safe\n// reorged — finalized observation reverted; back to 'pending' on next settle\nexport type GatewayGrantStatus =\n | \"pending\"\n | \"submitting\"\n | \"confirmed\"\n | \"finalized\"\n | \"reorged\";\n\nexport interface GatewayGrantResponse {\n id: string;\n grantorAddress: string;\n granteeId: string;\n // The signed scope entries, verbatim (`[operation:]scope`, bare = read).\n // This is the wire form: it is what the grantor signed and what the\n // gateway stores. Render `permissions` instead of parsing these.\n scopes: string[];\n // Derived by the SDK at read time from `scopes` via grantPermissions():\n // one `{ scope, actions }` row per scope pattern, canonically ordered.\n // Never sent to the gateway and never part of the signed payload. Absent\n // when `scopes` carries an entry this SDK version cannot interpret (an\n // operation introduced after this release) - fall back to `scopes` and\n // do not assume such a grant is read-only.\n permissions?: GrantPermission[];\n status: GatewayGrantStatus;\n addedAt: string;\n // Grantor-signed deadline. null = perpetual grant (signed value was 0).\n expiresAt: string | null;\n // Derived at read time from expiresAt vs the gateway's clock — a snapshot,\n // not a cached truth. Re-check against expiresAt locally if you care.\n expired: boolean;\n revokedAt: string | null;\n revocationSignature: string | null;\n // 'pending' until the grant registration fee is settled via /v1/escrow/pay.\n paymentStatus: \"pending\" | \"paid\";\n paidAt: string | null;\n paidBy: string | null;\n // Decimal-string uint256 monotonic nonce; advances on every state change.\n grantVersion: string;\n // Settle metadata — populated as the grant progresses through the chain\n // lifecycle. Null while `status === 'pending'`.\n settleTxHash: string | null;\n settleSubmittedAt: string | null;\n // Revocation metadata — populated independently when the grantor signs\n // and the gateway pushes a deregister tx.\n revocationTxHash: string | null;\n revocationSubmittedAt: string | null;\n fee: GatewayGrantFee;\n}\n\nexport type GrantListItem = GatewayGrantResponse;\n\n// Mirror of a DataRegistryV2 row as the gateway exposes it. `id` is the\n// deterministic `keccak256(abi.encode(owner, scope))` dataPointId — the same\n// value the contract uses as its primary key. `expectedVersion` is the latest\n// version the gateway has accepted; the on-chain row may be one version behind\n// while a settle is still pending. The gateway response intentionally drops\n// `status` from the body — read it from the on-chain contract when you need\n// the canonical lifecycle state.\nexport interface DataPointRecord {\n id: string;\n ownerAddress: string;\n scope: string;\n dataHash: string;\n metadataHash: string;\n // Decimal-string uint256.\n expectedVersion: string;\n // ISO 8601 timestamp of the most recent gateway-side upsert.\n addedAt: string;\n // ISO 8601 timestamp of the deletion tombstone, null/absent while live.\n // Only present on reads that opted in via `includeDeleted`; a plain\n // `getDataPoint` of a deleted row throws `DataPointDeletedError` instead.\n deletedAt?: string | null;\n}\n\nexport interface GetDataPointOptions {\n /**\n * Return the row even after it was deleted (with `deletedAt` set and the\n * tombstone hash pair) instead of throwing `DataPointDeletedError`.\n */\n includeDeleted?: boolean;\n}\n\nexport interface DataPointListResult {\n dataPoints: DataPointRecord[];\n cursor: string | null;\n}\n\nexport interface ListDataPointsOptions {\n /**\n * Only return rows added at or after this ISO 8601 timestamp. Used by sync\n * loops that want incremental tails — pass the last seen `addedAt`.\n */\n since?: string;\n /** Page size. Capped at 1000 by the gateway. */\n limit?: number;\n /**\n * Include deleted (tombstoned) rows, each carrying `deletedAt`. Off by\n * default; without it the SDK also drops any tombstone the gateway leaks.\n */\n includeDeleted?: boolean;\n}\n\n// grantVersion and expiresAt are decimal-string uint256s — same wire format\n// the gateway expects. The caller is responsible for converting their bigint\n// to a decimal string and for signing GRANT_REGISTRATION_TYPES with matching\n// bigint values.\nexport interface CreateGrantParams {\n grantorAddress: string;\n granteeId: string;\n scopes: string[];\n grantVersion: string;\n expiresAt: string;\n signature: string;\n}\n\nexport interface RevokeGrantParams {\n grantId: string;\n grantorAddress: string;\n grantVersion: string;\n signature: string;\n}\n\nexport interface RegisterServerParams {\n ownerAddress: string;\n serverAddress: string;\n publicKey: string;\n serverUrl: string;\n signature: string;\n}\n\nexport interface RegisterServerResult {\n serverId?: string;\n alreadyRegistered: boolean;\n}\n\nexport interface RegisterBuilderParams {\n ownerAddress: string;\n // Wallet the builder authenticates to the Personal Server with. The\n // builderId is deterministically derived from (owner, grantee, publicKey,\n // appUrl) so this triple pins the on-chain identity.\n granteeAddress: string;\n publicKey: string;\n appUrl: string;\n signature: string;\n}\n\nexport interface RegisterBuilderResult {\n builderId?: string;\n alreadyRegistered: boolean;\n}\n\n// AddData on DataRegistryV2. dataHash + metadataHash are bytes32 commitments\n// to the off-chain payload + its metadata. expectedVersion is a CAS knob —\n// the gateway/contract rejects on 409 if a higher version is already stored,\n// and the error body surfaces `currentExpectedVersion` so callers can re-sign.\nexport interface RegisterDataPointParams {\n ownerAddress: string;\n scope: string;\n dataHash: string;\n metadataHash: string;\n expectedVersion: string;\n signature: string;\n}\n\nexport interface RegisterDataPointResult {\n dataPointId?: string;\n expectedVersion?: string;\n}\n\n// Deletion = an owner-signed AddData for version current+1 carrying the\n// tombstone hash pair (see protocol/data-point-deletion.ts). The gateway\n// reconstructs the AddData from the body plus the tombstone constants and\n// recovers the signer, so the hashes are NOT on the wire. Used at\n// DELETE /v1/data/:dataPointId. expectedVersion is the tombstone's version\n// as a decimal uint256 string; the signature is the raw EIP-712 hex.\nexport interface DeleteDataPointParams {\n ownerAddress: string;\n scope: string;\n expectedVersion: string;\n signature: string;\n}\n\n// The tombstone version record the gateway returns on 200. Fields are\n// optional because, like RegisterDataPointResult, the SDK tolerates either\n// a bare body or an enveloped `{data}`; `deletedAt` is the durable marker.\nexport interface DeleteDataPointResult {\n dataPointId?: string;\n ownerAddress?: string;\n scope?: string;\n dataHash?: string;\n metadataHash?: string;\n expectedVersion?: string;\n deletedAt?: string | null;\n}\n\n// ── Escrow / data-access payment path ───────────────────────────────────────\n// /v1/escrow/pay debits the payer's escrow balance for a payable op. For a\n// Legacy grants use opType='grant' and opId=grantId. Standalone receipt-bound\n// reads use opType='data_access' and opId=accessRecord.recordId. amount and\n// paymentNonce are decimal uint256 strings on the wire. The signature is the\n// raw EIP-712 hex of GENERIC_PAYMENT_TYPES against escrowPaymentDomain.\n\n// A server-signed delivery receipt attached to a data-access payment. The\n// signature is over RECORD_DATA_ACCESS_TYPES against dataRegistryDomain; the\n// signer must be a personal server the data point's owner has registered as\n// trusted. Shape validation in clients does not verify this signature; the\n// gateway verifies it before re-using it on-chain in the next /v1/settle pass\n// via DataRegistryV2.recordDataAccess, where `recordId` dedupes via\n// `_usedRecordIds`.\nexport interface AccessRecord {\n dataPointId: string;\n // Decimal-string uint256 — the data point version being attested to.\n version: string;\n // Must equal the enclosing payment's payerAddress (the gateway enforces).\n accessor: string;\n recordId: string;\n signature: string;\n}\n\nexport interface PayForOperationParams {\n payerAddress: string;\n opType: string;\n opId: string;\n asset: string;\n amount: string;\n // Per-payer monotonic; (payer, nonce, kind) must be unique. The gateway\n // returns 409 if reused — bump and re-sign.\n paymentNonce: string;\n signature: string;\n // Optional: attach a server-signed access record so the next /v1/settle\n // pass submits a recordDataAccess tx alongside the payment settlement.\n // Required for data-access payments (the second-and-onward payments per\n // grant) that want their on-chain `totalAccesses` counter to advance.\n accessRecord?: AccessRecord;\n}\n\nexport interface PayForOperationResult {\n opType: string;\n opId: string;\n payerAddress: string;\n asset: string;\n amount: string;\n // Echoes how the gateway split this payment. `registrationPaid` is true on\n // the first payment for a grant (which bundles both fees) and false on\n // subsequent data-access-only payments. Off-chain ledger state only — the\n // on-chain settlement of the registration is tracked by the grant's\n // `status` field, not this flag.\n breakdown: {\n registrationFee: string;\n dataAccessFee: string;\n registrationPaid: boolean;\n };\n paymentNonce: string;\n paidAt: string;\n}\n\n// ── Settle / reconcile ──────────────────────────────────────────────────────\n// POST /v1/settle drains pending-on-chain rows (grants, servers, builders,\n// data points, data-point statuses, access records) to the relayer, then\n// promotes 'submitting' → 'confirmed' and 'confirmed' → 'finalized' for\n// previously-submitted rows. One call does all three; the response surfaces\n// each phase's outcomes.\n\n// The op-types the settle endpoint emits in `items[]`. Kept as a union so\n// callers can narrow inside the discriminated SettleItem shape. Mirrors the\n// gateway's drain phases (drainGrants/drainServers/drainBuilders/\n// drainDataPoints/drainDataPointStatuses/drainAccessRecords) — keep in sync\n// when the gateway adds a drain.\nexport type SettleOpType =\n | \"grant\"\n | \"server\"\n | \"data\"\n | \"access\"\n | \"builder\"\n | \"data-status\";\n\nexport type SettleItem =\n | {\n opType: SettleOpType;\n opId: string;\n // 'confirmed' when the submit function waited for the receipt and it\n // mined (registerAndSettle path); 'submitting' when only the tx was\n // sent (no receipt wait).\n status: \"submitting\" | \"confirmed\";\n settleTxHash: string | null;\n settleSubmittedAt: string | null;\n // Block height the tx mined in; only set when status === 'confirmed'.\n chainBlockHeight: string | null;\n revocationTxHash: string | null;\n revocationSubmittedAt: string | null;\n // True while lib/settle.ts is in placeholder mode for this row's pass.\n placeholder: boolean;\n }\n | {\n opType: SettleOpType;\n opId: string;\n status: \"skipped\";\n reason: string;\n }\n | {\n opType: SettleOpType;\n opId: string;\n status: \"failed\";\n error: string;\n };\n\n// Outcome of the housekeeping pass that retries earlier `submitting` rows\n// whose receipt arrived after the prior /v1/settle's wait budget elapsed.\nexport interface SettlePromoteResult {\n opType: SettleOpType;\n opId: string;\n status: \"confirmed\" | \"failed\" | \"pending\" | \"skipped\";\n txHash: string;\n chainBlockHeight: string | null;\n reason?: string;\n}\n\n// Outcome of the reconcile pass that advances 'confirmed' → 'finalized' once\n// the chain's finalized tip catches up past the tx's block (or reverts to\n// 'pending' on reorg detection).\nexport interface SettleReconcileItem {\n opId: string;\n status: \"finalized\" | \"reorged\" | \"unchanged\";\n chainBlockHeight: string | null;\n settleTxHash: string | null;\n reason?: string;\n}\n\nexport interface SettleParams {\n // Per-phase cap. Bounded by MAX_LIMIT on the gateway side; omit to use\n // the gateway's default BATCH_LIMIT.\n limit?: number;\n}\n\nexport interface SettleResult {\n scanned: number;\n submitted: number;\n confirmed: number;\n skipped: number;\n failed: number;\n items: SettleItem[];\n promoted: { count: number; items: SettlePromoteResult[] };\n reconciled: {\n scanned: number;\n finalized: number;\n reorged: number;\n unchanged: number;\n items: SettleReconcileItem[];\n };\n // Present only when the gateway is configured for paced submission —\n // spreads work across several blocks within one /v1/settle invocation.\n paced?: { iterations: number };\n}\n\n// /v1/escrow/balance?account=... — pure read. Returns finalized balances by\n// asset, plus the lifecycle breakdown of deposits.\nexport interface EscrowBalanceEntry {\n asset: string;\n // Gross credited deposits for (account, asset). Decremented only when the\n // reconcile pass marks a payment finalized — NOT on /v1/escrow/pay.\n balance: string;\n // Sum of claimedAmount for deposits still in 'submitted' status — surfaced\n // separately so clients don't conflate \"credited\" with \"deposit announced\n // but not yet confirmed.\"\n pendingAmount: string;\n // Sum of payments.amount for (account, asset) regardless of settled status —\n // mirrors the /v1/escrow/pay handler's soft-lock counter. Subtract from\n // `balance` to see how much the payer can still authorise.\n authorizedAmount: string;\n // `max(balance − authorizedAmount, 0)`. The headroom a payer has against\n // the soft-lock before /v1/escrow/pay starts returning 402.\n availableAmount: string;\n updatedAt: string | null;\n}\n\nexport interface EscrowDepositSubmitted {\n txHash: string;\n submittedAt: string;\n claimedAsset: string;\n claimedAmount: string;\n}\n\nexport interface EscrowDepositFinalized {\n txHash: string;\n finalizedAt: string | null;\n blockNumber: string | null;\n claimedAsset: string;\n claimedAmount: string;\n}\n\nexport interface EscrowDepositFailed {\n txHash: string;\n submittedAt: string;\n claimedAsset: string;\n claimedAmount: string;\n lastError: string | null;\n}\n\nexport interface EscrowBalance {\n account: string;\n balances: EscrowBalanceEntry[];\n deposits: {\n submitted: EscrowDepositSubmitted[];\n finalized: EscrowDepositFinalized[];\n failed: EscrowDepositFailed[];\n };\n}\n\n// /v1/escrow/deposit announces an on-chain deposit tx so the gateway can\n// reconcile it into the payer's balance. The gateway extracts the credited\n// account from calldata — no off-chain claim about who paid.\nexport interface SubmitDepositParams {\n txHash: string;\n}\n\nexport interface DepositState {\n txHash: string;\n account: string;\n // 'submitted' | 'finalized' | 'failed' — kept open since the gateway adds\n // states (e.g. 'orphaned') as the deposit flow evolves.\n status: string;\n blockNumber: string | null;\n submittedAt: string;\n finalizedAt: string | null;\n lastError: string | null;\n}\n\nexport interface GatewayClient {\n isRegisteredBuilder(address: string): Promise<boolean>;\n getBuilder(address: string): Promise<Builder | null>;\n getGrant(grantId: string): Promise<GatewayGrantResponse | null>;\n listGrantsByUser(userAddress: string): Promise<GrantListItem[]>;\n getSchemaForScope(scope: string): Promise<Schema | null>;\n getServer(address: string): Promise<ServerInfo | null>;\n /**\n * List every personal server an owner has registered — split into `active`\n * (currently trusted) and `revoked` (deregistered). Each list is ordered\n * newest-first; `active[0]` is the sensible default when the caller just\n * needs one URL for the owner. Empty owner returns `{active: [], revoked: [], count: 0}`.\n * Discovery-only endpoint — no per-server attestation; use `getServer` for that.\n */\n listServersByOwner(owner: string): Promise<OwnerServersResult>;\n /**\n * Fetch a single data point by its deterministic id (keccak256 of (owner, scope)).\n * Returns null on 404. Throws `DataPointDeletedError` on 410, or when the row is a\n * tombstone, unless `options.includeDeleted` is set. The gateway omits `status` from\n * the response body -- read it from the on-chain DataRegistryV2 contract when you need\n * the canonical lifecycle state.\n */\n getDataPoint(\n dataPointId: string,\n options?: GetDataPointOptions,\n ): Promise<DataPointRecord | null>;\n /**\n * Page through an owner's data points. Cursor is opaque; pass `null` for the first\n * page and feed back `result.cursor` until it returns null.\n * Deleted rows are excluded unless `options.includeDeleted` is set.\n */\n listDataPointsByOwner(\n owner: string,\n cursor: string | null,\n options?: ListDataPointsOptions,\n ): Promise<DataPointListResult>;\n getSchema(schemaId: string): Promise<Schema | null>;\n registerServer(params: RegisterServerParams): Promise<RegisterServerResult>;\n registerBuilder(\n params: RegisterBuilderParams,\n ): Promise<RegisterBuilderResult>;\n registerDataPoint(\n params: RegisterDataPointParams,\n ): Promise<RegisterDataPointResult>;\n /**\n * Tombstone a data point: `DELETE /v1/data/:dataPointId` with the owner's\n * AddData signature for version `current + 1`. The dataPointId is derived\n * from (ownerAddress, scope). Throws `DataPointVersionConflictError` on\n * 409, `DataPointDeletedError` on 410 (already deleted),\n * `DataPointNotFoundError` on 404.\n */\n deleteDataPoint(\n params: DeleteDataPointParams,\n ): Promise<DeleteDataPointResult>;\n createGrant(params: CreateGrantParams): Promise<{ grantId?: string }>;\n revokeGrant(params: RevokeGrantParams): Promise<void>;\n getEscrowBalance(account: string): Promise<EscrowBalance>;\n submitEscrowDeposit(params: SubmitDepositParams): Promise<DepositState>;\n payForOperation(\n params: PayForOperationParams,\n ): Promise<PayForOperationResult>;\n settle(params?: SettleParams): Promise<SettleResult>;\n}\n\n// Attach the derived `permissions` view to a grant record read back from the\n// gateway. The wire `scopes` are left untouched.\nfunction withGrantPermissions<T extends GatewayGrantResponse>(grant: T): T {\n // Gateway responses are untrusted runtime data despite their type. The\n // `permissions` view is SDK-owned: drop whatever the body carried under\n // that name, then derive only from a well-formed string[] and leave the\n // field absent otherwise, so a spoofed or stale value can never be\n // rendered as this grant's authority.\n const stripped = { ...grant };\n delete stripped.permissions;\n const scopes: unknown = stripped.scopes;\n if (!Array.isArray(scopes)) {\n return stripped;\n }\n const permissions = tryGrantPermissions(scopes as string[]);\n return permissions === undefined ? stripped : { ...stripped, permissions };\n}\n\nexport function createGatewayClient(baseUrl: string): GatewayClient {\n const base = baseUrl.replace(/\\/+$/, \"\");\n\n function malformedBody(res: Response): Error {\n return new Error(\n `Gateway error: ${res.status} malformed response body (expected a JSON envelope)`,\n );\n }\n\n // GET responses are `{ data, proof, pagination? }`. A 200 whose body is\n // empty, not JSON, or not an object with `data` is a gateway bug, not a\n // record: fail with a clear error instead of returning undefined.\n async function readEnvelope<T>(\n res: Response,\n ): Promise<Record<string, unknown> & { data: T }> {\n const envelope = await readJsonValue(res);\n if (!isPlainObject(envelope) || !(\"data\" in envelope)) {\n throw malformedBody(res);\n }\n return envelope as Record<string, unknown> & { data: T };\n }\n\n async function unwrapEnvelope<T>(res: Response): Promise<T> {\n return (await readEnvelope<T>(res)).data;\n }\n\n function getMutationId(body: unknown, key: string): string | undefined {\n if (!isPlainObject(body)) return undefined;\n const value = body[key] ?? body[\"id\"];\n return typeof value === \"string\" ? value : undefined;\n }\n\n // Mutation responses are bare JSON; GET responses are enveloped. Accept\n // either so a gateway that wraps the tombstone record still round-trips.\n // Any body that is not a plain object (empty 204, `null`, an array, an\n // HTML error page) reads as `{}` so the status-code mapping still wins.\n async function readBody(res: Response): Promise<Record<string, unknown>> {\n const raw = await readJsonObject(res);\n const data = raw[\"data\"];\n if (isPlainObject(data) && \"proof\" in raw) {\n return data;\n }\n return raw;\n }\n\n function stringOrUndefined(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n }\n\n async function deletedError(\n res: Response,\n details: { dataPointId?: string; scope?: string; ownerAddress?: string },\n ): Promise<DataPointDeletedError> {\n const body = await readBody(res);\n return new DataPointDeletedError(\n `Data point ${details.dataPointId ?? details.scope ?? \"\"} has been deleted`,\n { ...details, deletedAt: tombstoneDeletedAt(body) },\n );\n }\n\n return {\n async isRegisteredBuilder(address: string): Promise<boolean> {\n const builder = await this.getBuilder(address);\n return builder !== null;\n },\n\n async getBuilder(address: string): Promise<Builder | null> {\n const res = await fetch(`${base}/v1/builders/${address}`);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return unwrapEnvelope<Builder>(res);\n },\n\n async getGrant(grantId: string): Promise<GatewayGrantResponse | null> {\n const res = await fetch(`${base}/v1/grants/${grantId}`);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return withGrantPermissions(\n await unwrapEnvelope<GatewayGrantResponse>(res),\n );\n },\n\n async listGrantsByUser(userAddress: string): Promise<GrantListItem[]> {\n const res = await fetch(`${base}/v1/grants?user=${userAddress}`);\n if (res.status === 404) return [];\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n const grants = await unwrapEnvelope<GrantListItem[]>(res);\n return grants.map(withGrantPermissions);\n },\n\n async getSchemaForScope(scope: string): Promise<Schema | null> {\n const res = await fetch(`${base}/v1/schemas?scope=${scope}`);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return unwrapEnvelope<Schema>(res);\n },\n\n async getServer(address: string): Promise<ServerInfo | null> {\n const res = await fetch(`${base}/v1/servers/${address}`);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return unwrapEnvelope<ServerInfo>(res);\n },\n\n async listServersByOwner(owner: string): Promise<OwnerServersResult> {\n // URLSearchParams encodes the owner so a malformed value (e.g.\n // \"0xabc&foo=bar\") can't inject extra query params. Same pattern as\n // listDataPointsByOwner.\n const params = new URLSearchParams({ owner });\n const res = await fetch(`${base}/v1/servers?${params.toString()}`);\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n // Unlike /v1/servers/:address, the list endpoint returns the body\n // directly — no GatewayEnvelope, no attestation (discovery-only).\n return (await res.json()) as OwnerServersResult;\n },\n\n async getDataPoint(\n dataPointId: string,\n options?: GetDataPointOptions,\n ): Promise<DataPointRecord | null> {\n const query = options?.includeDeleted ? \"?includeDeleted=true\" : \"\";\n const res = await fetch(`${base}/v1/data/${dataPointId}${query}`);\n if (res.status === 404) return null;\n if (res.status === 410) {\n throw await deletedError(res, { dataPointId });\n }\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n const record = await unwrapEnvelope<DataPointRecord>(res);\n // Never hand a tombstone back as data unless the caller opted in --\n // guards against a gateway that returns 200 + deletedAt on plain reads.\n if (!options?.includeDeleted && isDataPointTombstone(record)) {\n throw new DataPointDeletedError(\n `Data point ${dataPointId} has been deleted`,\n {\n dataPointId,\n scope: record.scope,\n ownerAddress: record.ownerAddress,\n deletedAt: tombstoneDeletedAt(record),\n },\n );\n }\n return record;\n },\n\n async listDataPointsByOwner(\n owner: string,\n cursor: string | null,\n options?: ListDataPointsOptions,\n ): Promise<DataPointListResult> {\n const params = new URLSearchParams({ user: owner });\n if (cursor !== null) {\n params.set(\"cursor\", cursor);\n }\n if (options?.since) {\n params.set(\"since\", options.since);\n }\n if (options?.limit !== undefined) {\n params.set(\"limit\", String(options.limit));\n }\n if (options?.includeDeleted) {\n params.set(\"includeDeleted\", \"true\");\n }\n const res = await fetch(`${base}/v1/data?${params.toString()}`);\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n // Next-page cursor lives in the envelope's `pagination.nextCursor`\n // (a sibling of `data`), so read the full envelope rather than going\n // through `unwrapEnvelope`, which returns only `data`.\n const envelope = await readEnvelope<unknown>(res);\n if (\n !isPlainObject(envelope.data) ||\n !Array.isArray(envelope.data[\"dataPoints\"])\n ) {\n throw malformedBody(res);\n }\n const rows = envelope.data[\"dataPoints\"] as DataPointRecord[];\n const pagination = isPlainObject(envelope[\"pagination\"])\n ? envelope[\"pagination\"]\n : undefined;\n const rawCursor = pagination?.[\"nextCursor\"];\n const nextCursor =\n pagination?.[\"hasMore\"] === false || typeof rawCursor !== \"string\"\n ? null\n : rawCursor;\n // Tombstones only surface when asked for; drop any the gateway leaks\n // on a plain list so sync loops never ingest one as data.\n const dataPoints = options?.includeDeleted\n ? rows\n : rows.filter((row) => !isDataPointTombstone(row));\n return {\n dataPoints,\n cursor: nextCursor,\n };\n },\n\n async getSchema(schemaId: string): Promise<Schema | null> {\n const res = await fetch(`${base}/v1/schemas/${schemaId}`);\n if (res.status === 404) return null;\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return unwrapEnvelope<Schema>(res);\n },\n\n async registerServer(\n params: RegisterServerParams,\n ): Promise<RegisterServerResult> {\n const res = await fetch(`${base}/v1/servers`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${params.signature}`,\n },\n body: JSON.stringify({\n ownerAddress: params.ownerAddress,\n serverAddress: params.serverAddress,\n publicKey: params.publicKey,\n serverUrl: params.serverUrl,\n }),\n });\n if (res.status === 409) {\n const body = await readJsonObject(res);\n return {\n serverId: getMutationId(body, \"serverId\"),\n alreadyRegistered: true,\n };\n }\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n const body = await readJsonObject(res);\n return {\n serverId: getMutationId(body, \"serverId\"),\n alreadyRegistered: false,\n };\n },\n\n async registerBuilder(\n params: RegisterBuilderParams,\n ): Promise<RegisterBuilderResult> {\n const res = await fetch(`${base}/v1/builders`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${params.signature}`,\n },\n body: JSON.stringify({\n ownerAddress: params.ownerAddress,\n granteeAddress: params.granteeAddress,\n publicKey: params.publicKey,\n appUrl: params.appUrl,\n }),\n });\n // 409 is idempotent — the gateway's current 409 body doesn't include\n // the builderId, but we tolerate it in case that changes (mirrors the\n // registerServer / createGrant shape).\n if (res.status === 409) {\n const body = await readJsonObject(res);\n return {\n builderId: getMutationId(body, \"builderId\"),\n alreadyRegistered: true,\n };\n }\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n const body = await readJsonObject(res);\n return {\n builderId: getMutationId(body, \"builderId\"),\n alreadyRegistered: false,\n };\n },\n\n async registerDataPoint(\n params: RegisterDataPointParams,\n ): Promise<RegisterDataPointResult> {\n const res = await fetch(`${base}/v1/data`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${params.signature}`,\n },\n body: JSON.stringify({\n ownerAddress: params.ownerAddress,\n scope: params.scope,\n dataHash: params.dataHash,\n metadataHash: params.metadataHash,\n expectedVersion: params.expectedVersion,\n }),\n });\n // 409 is a real failure here (stale CAS), not an idempotent replay —\n // surface the gateway's error message verbatim so the caller knows\n // what `currentExpectedVersion` to re-sign against.\n if (!res.ok) {\n const body = await readJsonObject(res);\n const detail = stringOrUndefined(body[\"error\"]) ?? res.statusText;\n throw new Error(`Gateway error: ${res.status} ${detail}`);\n }\n const body = await readJsonObject(res);\n return {\n dataPointId: getMutationId(body, \"dataPointId\"),\n expectedVersion: stringOrUndefined(body[\"expectedVersion\"]),\n };\n },\n\n async deleteDataPoint(\n params: DeleteDataPointParams,\n ): Promise<DeleteDataPointResult> {\n // The gateway keys the row on keccak256(abi.encode(owner, scope)) --\n // derive it locally rather than asking the caller for it, so the path\n // can never disagree with the signed (owner, scope).\n const dataPointId = deriveDataPointId(\n params.ownerAddress as `0x${string}`,\n params.scope,\n );\n const details = {\n dataPointId,\n scope: params.scope,\n ownerAddress: params.ownerAddress,\n };\n // Signature rides both in the body (the DELETE contract) and as the\n // Web3Signed Authorization header every other /v1 mutation uses.\n const res = await fetch(`${base}/v1/data/${dataPointId}`, {\n method: \"DELETE\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${params.signature}`,\n },\n body: JSON.stringify({\n ownerAddress: params.ownerAddress,\n scope: params.scope,\n expectedVersion: params.expectedVersion,\n signature: params.signature,\n }),\n });\n if (res.status === 404) {\n throw new DataPointNotFoundError(\n `Data point ${dataPointId} (scope '${params.scope}') is not registered`,\n details,\n );\n }\n if (res.status === 409) {\n const body = await readBody(res);\n const currentExpectedVersion = stringOrUndefined(\n body[\"currentExpectedVersion\"],\n );\n const detail = stringOrUndefined(body[\"error\"]) ?? res.statusText;\n throw new DataPointVersionConflictError(\n `Gateway error: 409 ${detail}`,\n {\n ...details,\n expectedVersion: params.expectedVersion,\n currentExpectedVersion,\n },\n );\n }\n if (res.status === 410) {\n throw await deletedError(res, details);\n }\n if (!res.ok) {\n // 400 = the recovered signer/hashes do not match the tombstone\n // AddData. Surface the gateway's message verbatim, like\n // registerDataPoint does.\n const body = await readBody(res);\n const detail = stringOrUndefined(body[\"error\"]) ?? res.statusText;\n throw new Error(`Gateway error: ${res.status} ${detail}`);\n }\n const body = await readBody(res);\n return {\n dataPointId: getMutationId(body, \"dataPointId\") ?? dataPointId,\n ownerAddress: stringOrUndefined(body[\"ownerAddress\"]),\n scope: stringOrUndefined(body[\"scope\"]),\n dataHash: stringOrUndefined(body[\"dataHash\"]),\n metadataHash: stringOrUndefined(body[\"metadataHash\"]),\n expectedVersion: stringOrUndefined(body[\"expectedVersion\"]),\n deletedAt: tombstoneDeletedAt(body),\n };\n },\n\n async createGrant(\n params: CreateGrantParams,\n ): Promise<{ grantId?: string }> {\n const res = await fetch(`${base}/v1/grants`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${params.signature}`,\n },\n body: JSON.stringify({\n grantorAddress: params.grantorAddress,\n granteeId: params.granteeId,\n scopes: params.scopes,\n grantVersion: params.grantVersion,\n expiresAt: params.expiresAt,\n }),\n });\n if (res.status === 409) {\n const body = await readJsonObject(res);\n return { grantId: getMutationId(body, \"grantId\") };\n }\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n const body = await readJsonObject(res);\n return { grantId: getMutationId(body, \"grantId\") };\n },\n\n async revokeGrant(params: RevokeGrantParams): Promise<void> {\n const res = await fetch(`${base}/v1/grants/${params.grantId}`, {\n method: \"DELETE\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${params.signature}`,\n },\n body: JSON.stringify({\n grantorAddress: params.grantorAddress,\n grantVersion: params.grantVersion,\n }),\n });\n if (res.status === 409) return;\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n },\n\n async getEscrowBalance(account: string): Promise<EscrowBalance> {\n const res = await fetch(`${base}/v1/escrow/balance?account=${account}`);\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n // Unlike the rest of /v1, the balance endpoint returns the body\n // directly (no GatewayEnvelope wrap) — it's a pure read with no\n // gateway-signed attestation. See data-gateway api/v1/escrow/balance.ts.\n return (await res.json()) as EscrowBalance;\n },\n\n async submitEscrowDeposit(\n params: SubmitDepositParams,\n ): Promise<DepositState> {\n const res = await fetch(`${base}/v1/escrow/deposit`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ txHash: params.txHash }),\n });\n // The gateway returns 202 for \"accepted (still confirming)\" and 200 for\n // duplicate idempotent replays. Both carry the deposit's current state.\n if (res.status !== 200 && res.status !== 202) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as DepositState;\n },\n\n async payForOperation(\n params: PayForOperationParams,\n ): Promise<PayForOperationResult> {\n // Build the body without the accessRecord key when absent so the\n // gateway's \"missing optional\" branch matches the no-receipt case\n // exactly (an explicit `accessRecord: undefined` would JSON-serialize\n // to nothing — same result — but keeping it conditional makes wire\n // traces easier to read).\n const body: Record<string, unknown> = {\n payerAddress: params.payerAddress,\n opType: params.opType,\n opId: params.opId,\n asset: params.asset,\n amount: params.amount,\n paymentNonce: params.paymentNonce,\n };\n if (params.accessRecord) {\n body[\"accessRecord\"] = params.accessRecord;\n }\n const res = await fetch(`${base}/v1/escrow/pay`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Web3Signed ${params.signature}`,\n },\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as PayForOperationResult;\n },\n\n async settle(params?: SettleParams): Promise<SettleResult> {\n const res = await fetch(`${base}/v1/settle`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n // The gateway accepts an empty body; only `limit` is recognised.\n // Always send a JSON body so the gateway's req.body shape parse\n // doesn't have to deal with an undefined.\n body: JSON.stringify(params ?? {}),\n });\n if (!res.ok) {\n throw new Error(`Gateway error: ${res.status} ${res.statusText}`);\n }\n return (await res.json()) as SettleResult;\n },\n };\n}\n"],"mappings":"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","names":["body"]}
|