otto-intel-mcp 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -18
- package/dist/account-userop-policy-definition.d.ts +14 -0
- package/dist/account-userop-policy-definition.js +12 -0
- package/dist/account-userop-policy-relation.d.ts +371 -0
- package/dist/account-userop-policy-relation.js +87 -0
- package/dist/adapter/cdp-signer.d.ts +76 -9
- package/dist/adapter/cdp-signer.js +274 -33
- package/dist/adapter/evm-call-failure.d.ts +29 -0
- package/dist/adapter/evm-call-failure.js +109 -0
- package/dist/adapter/refusal.d.ts +1 -1
- package/dist/adapter/refusal.js +13 -0
- package/dist/adapter/route-simulator.d.ts +91 -0
- package/dist/adapter/route-simulator.js +143 -0
- package/dist/adapter-cdp-index.d.ts +1 -1
- package/dist/executable-equities.d.ts +55 -0
- package/dist/executable-equities.js +116 -0
- package/dist/executable-yield-markets.d.ts +261 -0
- package/dist/executable-yield-markets.js +154 -0
- package/dist/execution-account-gas-quote.d.ts +141 -0
- package/dist/execution-account-gas-quote.js +443 -0
- package/dist/execution-account-gas-review.d.ts +123 -0
- package/dist/execution-account-gas-review.js +104 -0
- package/dist/execution-account-gas-runtime.d.ts +33 -0
- package/dist/execution-account-gas-runtime.js +131 -0
- package/dist/execution-account-gas-view.d.ts +936 -0
- package/dist/execution-account-gas-view.js +56 -0
- package/dist/execution-config.d.ts +3 -0
- package/dist/execution-config.js +4 -0
- package/dist/execution-delegated-definition.d.ts +7 -6
- package/dist/execution-delegated-definition.js +13 -2
- package/dist/execution-delegation-admin-definition.d.ts +127 -5
- package/dist/execution-delegation-admin-definition.js +155 -5
- package/dist/execution-delegation-admin.d.ts +48 -2
- package/dist/execution-delegation-admin.js +422 -12
- package/dist/execution-delegation-cap-store.d.ts +386 -0
- package/dist/execution-delegation-cap-store.js +1150 -0
- package/dist/execution-delegation-exit-definition.d.ts +309 -0
- package/dist/execution-delegation-exit-definition.js +204 -0
- package/dist/execution-delegation-exit-store.d.ts +114 -0
- package/dist/execution-delegation-exit-store.js +671 -0
- package/dist/execution-delegation-exit.d.ts +4 -0
- package/dist/execution-delegation-exit.js +614 -0
- package/dist/execution-delegation-finality.d.ts +22 -0
- package/dist/execution-delegation-finality.js +90 -0
- package/dist/execution-delegation-operation-store.d.ts +129 -0
- package/dist/execution-delegation-operation-store.js +1107 -0
- package/dist/execution-delegation-operations-definition.d.ts +282 -0
- package/dist/execution-delegation-operations-definition.js +117 -0
- package/dist/execution-delegation-operations.d.ts +6 -0
- package/dist/execution-delegation-operations.js +282 -0
- package/dist/execution-delegation-policy.d.ts +154 -8
- package/dist/execution-delegation-policy.js +273 -40
- package/dist/execution-delegation.d.ts +51 -0
- package/dist/execution-delegation.js +441 -31
- package/dist/execution-errors.d.ts +1 -1
- package/dist/execution-errors.js +122 -0
- package/dist/execution-index.d.ts +7 -2
- package/dist/execution-index.js +7 -2
- package/dist/execution-journal-schema.d.ts +96 -0
- package/dist/execution-journal-schema.js +368 -0
- package/dist/execution-journal-tables.d.ts +14 -0
- package/dist/execution-journal-tables.js +227 -0
- package/dist/execution-policy-compatibility.d.ts +34 -0
- package/dist/execution-policy-compatibility.js +76 -0
- package/dist/execution-registration.d.ts +54 -2
- package/dist/execution-registration.js +151 -5
- package/dist/execution-sponsored-account-profile.d.ts +32 -0
- package/dist/execution-sponsored-account-profile.js +59 -0
- package/dist/execution-sponsored-account.d.ts +1519 -0
- package/dist/execution-sponsored-account.js +222 -0
- package/dist/execution-sponsored-definition.d.ts +1713 -0
- package/dist/execution-sponsored-definition.js +185 -0
- package/dist/execution-sponsored-gas-policy.d.ts +27 -0
- package/dist/execution-sponsored-gas-policy.js +110 -0
- package/dist/execution-sponsored-hosted.d.ts +3 -0
- package/dist/execution-sponsored-hosted.js +71 -0
- package/dist/execution-sponsored-index.d.ts +1322 -0
- package/dist/execution-sponsored-index.js +102 -0
- package/dist/execution-sponsored-mcp-definition.d.ts +1662 -0
- package/dist/execution-sponsored-mcp-definition.js +94 -0
- package/dist/execution-sponsored-receipt.d.ts +47 -0
- package/dist/execution-sponsored-receipt.js +74 -0
- package/dist/execution-sponsored-registration.d.ts +14 -0
- package/dist/execution-sponsored-registration.js +77 -0
- package/dist/execution-sponsored-runtime.d.ts +84 -0
- package/dist/execution-sponsored-runtime.js +149 -0
- package/dist/execution-sponsored-settlement.d.ts +15 -0
- package/dist/execution-sponsored-settlement.js +88 -0
- package/dist/execution-sponsored-store.d.ts +67 -0
- package/dist/execution-sponsored-store.js +517 -0
- package/dist/execution-sponsored-transport.d.ts +90 -0
- package/dist/execution-sponsored-transport.js +180 -0
- package/dist/execution-tool-definitions.d.ts +21 -21
- package/dist/execution-tool-definitions.js +0 -7
- package/dist/execution-tools.d.ts +53 -1
- package/dist/execution-tools.js +171 -13
- package/dist/execution-worker-guard.d.ts +28 -0
- package/dist/execution-worker-guard.js +124 -0
- package/dist/hybrid-yield-index.d.ts +10 -0
- package/dist/hybrid-yield-index.js +11 -0
- package/dist/hybrid-yield-markets.d.ts +96 -0
- package/dist/hybrid-yield-markets.js +124 -0
- package/dist/hybrid-yield.d.ts +411 -0
- package/dist/hybrid-yield.js +155 -0
- package/dist/hyperliquid-order-builder.js +4 -0
- package/dist/lifi-execution-client.d.ts +6 -0
- package/dist/lifi-execution-client.js +3 -0
- package/dist/lp-userop-account.d.ts +461 -0
- package/dist/lp-userop-account.js +417 -0
- package/dist/lp-userop-authority.d.ts +49 -0
- package/dist/lp-userop-authority.js +211 -0
- package/dist/lp-userop-definition.d.ts +233 -0
- package/dist/lp-userop-definition.js +85 -0
- package/dist/lp-userop-hosted.d.ts +3 -0
- package/dist/lp-userop-hosted.js +73 -0
- package/dist/lp-userop-index.d.ts +1569 -0
- package/dist/lp-userop-index.js +112 -0
- package/dist/lp-userop-mcp-definition.d.ts +1220 -0
- package/dist/lp-userop-mcp-definition.js +46 -0
- package/dist/lp-userop-operation.d.ts +1230 -0
- package/dist/lp-userop-operation.js +140 -0
- package/dist/lp-userop-registration.d.ts +14 -0
- package/dist/lp-userop-registration.js +70 -0
- package/dist/lp-userop-settlement.d.ts +89 -0
- package/dist/lp-userop-settlement.js +184 -0
- package/dist/lp-userop.d.ts +643 -0
- package/dist/lp-userop.js +343 -0
- package/dist/native-yield-account-definition.d.ts +1192 -0
- package/dist/native-yield-account-definition.js +232 -0
- package/dist/native-yield-account-gas.d.ts +58 -0
- package/dist/native-yield-account-gas.js +228 -0
- package/dist/native-yield-account-index.d.ts +23 -0
- package/dist/native-yield-account-index.js +28 -0
- package/dist/native-yield-account-policy.d.ts +168 -0
- package/dist/native-yield-account-policy.js +525 -0
- package/dist/native-yield-account-rpc.d.ts +8 -0
- package/dist/native-yield-account-rpc.js +128 -0
- package/dist/native-yield-account-runtime.d.ts +71 -0
- package/dist/native-yield-account-runtime.js +125 -0
- package/dist/native-yield-account-store.d.ts +40 -0
- package/dist/native-yield-account-store.js +307 -0
- package/dist/native-yield-account.d.ts +662 -0
- package/dist/native-yield-account.js +371 -0
- package/dist/native-yield-consent-store.d.ts +20 -0
- package/dist/native-yield-consent-store.js +317 -0
- package/dist/native-yield-definition.d.ts +288 -0
- package/dist/native-yield-definition.js +105 -0
- package/dist/native-yield-reader.d.ts +4 -0
- package/dist/native-yield-reader.js +137 -0
- package/dist/native-yield-settlement.d.ts +48 -0
- package/dist/native-yield-settlement.js +167 -0
- package/dist/native-yield-simulation.d.ts +18 -0
- package/dist/native-yield-simulation.js +45 -0
- package/dist/native-yield-userop-account.d.ts +675 -0
- package/dist/native-yield-userop-account.js +357 -0
- package/dist/native-yield-userop-authority.d.ts +214 -0
- package/dist/native-yield-userop-authority.js +296 -0
- package/dist/native-yield-userop-definition.d.ts +331 -0
- package/dist/native-yield-userop-definition.js +105 -0
- package/dist/native-yield-userop-hosted.d.ts +3 -0
- package/dist/native-yield-userop-hosted.js +71 -0
- package/dist/native-yield-userop-index.d.ts +1522 -0
- package/dist/native-yield-userop-index.js +142 -0
- package/dist/native-yield-userop-mcp-definition.d.ts +1356 -0
- package/dist/native-yield-userop-mcp-definition.js +50 -0
- package/dist/native-yield-userop-operation.d.ts +845 -0
- package/dist/native-yield-userop-operation.js +127 -0
- package/dist/native-yield-userop-policy.d.ts +23 -0
- package/dist/native-yield-userop-policy.js +25 -0
- package/dist/native-yield-userop-registration.d.ts +17 -0
- package/dist/native-yield-userop-registration.js +76 -0
- package/dist/native-yield-userop-settlement.d.ts +8 -0
- package/dist/native-yield-userop-settlement.js +79 -0
- package/dist/native-yield-userop-simulation.d.ts +18 -0
- package/dist/native-yield-userop-simulation.js +34 -0
- package/dist/native-yield-userop.d.ts +35 -0
- package/dist/native-yield-userop.js +156 -0
- package/dist/native-yield-verify.d.ts +30 -0
- package/dist/native-yield-verify.js +141 -0
- package/dist/native-yield.d.ts +17 -0
- package/dist/native-yield.js +119 -0
- package/dist/stdio-main.js +0 -0
- package/dist/stock-buy-definition.d.ts +338 -0
- package/dist/stock-buy-definition.js +127 -0
- package/dist/stock-buy-oracle.d.ts +8 -0
- package/dist/stock-buy-oracle.js +22 -0
- package/dist/stock-buy.d.ts +17 -0
- package/dist/stock-buy.js +164 -0
- package/dist/tool-definitions.d.ts +3 -1
- package/dist/tool-definitions.js +55 -2
- package/dist/vendor/lp-v3/lp-v3-atomic-contract.d.ts +445 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-contract.js +90 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-evidence.d.ts +14 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-evidence.js +82 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-index.d.ts +80 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-index.js +19 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-plan.d.ts +43 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-plan.js +112 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-contract.d.ts +312 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-contract.js +41 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-index.d.ts +3 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-index.js +3 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-rpc.d.ts +49 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read-rpc.js +88 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read.d.ts +17 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-read.js +232 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-settlement.d.ts +105 -0
- package/dist/vendor/lp-v3/lp-v3-atomic-settlement.js +162 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-contract.d.ts +606 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-contract.js +59 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-index.d.ts +168 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-index.js +23 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-observation.d.ts +32 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-observation.js +228 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-plan.d.ts +11 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-plan.js +91 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-reconcile.d.ts +9 -0
- package/dist/vendor/lp-v3/lp-v3-collect-protocol-reconcile.js +124 -0
- package/dist/vendor/lp-v3/lp-v3-contract.d.ts +538 -0
- package/dist/vendor/lp-v3/lp-v3-contract.js +137 -0
- package/dist/vendor/lp-v3/lp-v3-evidence.d.ts +25 -0
- package/dist/vendor/lp-v3/lp-v3-evidence.js +168 -0
- package/dist/vendor/lp-v3/lp-v3-plan.d.ts +22 -0
- package/dist/vendor/lp-v3/lp-v3-plan.js +135 -0
- package/dist/vendor/lp-v3/types.d.ts +131 -0
- package/dist/vendor/lp-v3/types.js +6 -0
- package/dist/vendor/lp-v3/v4-math.d.ts +18 -0
- package/dist/vendor/lp-v3/v4-math.js +90 -0
- package/dist/yield-deposit-definition.d.ts +374 -0
- package/dist/yield-deposit-definition.js +123 -0
- package/dist/yield-deposit.d.ts +16 -0
- package/dist/yield-deposit.js +149 -0
- package/dist/yield-route-semantics.d.ts +27 -0
- package/dist/yield-route-semantics.js +71 -0
- package/dist/yield-withdraw-definition.d.ts +357 -0
- package/dist/yield-withdraw-definition.js +90 -0
- package/dist/yield-withdraw.d.ts +11 -0
- package/dist/yield-withdraw.js +122 -0
- package/package.json +239 -7
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/** Guarded LP collection is record v3 in the existing sponsored claim and gas ledger. */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { hybridYieldCommitment } from './hybrid-yield-markets.js';
|
|
4
|
+
import { SPONSORED_OPERATION_SCHEMA, SPONSORED_AUTHORITY_SCHEMA, SPONSORED_ACTIVATION_SCHEMA, sponsoredDigest, sponsoredChainTerms, sponsoredActivationSupports } from './execution-sponsored-definition.js';
|
|
5
|
+
import { SPONSORED_CDP_NETWORKS } from './execution-sponsored-transport.js';
|
|
6
|
+
import { LP_USEROP_REVIEW_RECORD_SCHEMA } from './lp-userop-definition.js';
|
|
7
|
+
import { LP_USEROP_ENVELOPE_SCHEMA, LP_USEROP_SUBMIT_EVIDENCE_SCHEMA, assertLpUserOpEnvelope, lpUserOpRecordedCalls } from './lp-userop.js';
|
|
8
|
+
import { LP_USEROP_SETTLEMENT_SCHEMA } from './lp-userop-settlement.js';
|
|
9
|
+
import { commitment as protocolCommitment } from './vendor/lp-v3/lp-v3-contract.js';
|
|
10
|
+
const time = z.number().int().positive().safe();
|
|
11
|
+
export const LP_USEROP_CONTEXT_SCHEMA = z.object({ kind: z.literal('lp-collect-userop'),
|
|
12
|
+
account: SPONSORED_AUTHORITY_SCHEMA.shape.account, chainId: z.literal(8453),
|
|
13
|
+
envelope: LP_USEROP_ENVELOPE_SCHEMA, review: LP_USEROP_REVIEW_RECORD_SCHEMA,
|
|
14
|
+
authority: SPONSORED_AUTHORITY_SCHEMA, activation: SPONSORED_ACTIVATION_SCHEMA,
|
|
15
|
+
reviewedAtMs: time, validUntilMs: time,
|
|
16
|
+
}).strict();
|
|
17
|
+
export const LP_USEROP_OPERATION_SCHEMA = SPONSORED_OPERATION_SCHEMA.extend({ version: z.literal(3),
|
|
18
|
+
context: LP_USEROP_CONTEXT_SCHEMA, submitEvidence: LP_USEROP_SUBMIT_EVIDENCE_SCHEMA.optional(),
|
|
19
|
+
settlement: LP_USEROP_SETTLEMENT_SCHEMA.optional(),
|
|
20
|
+
}).strict();
|
|
21
|
+
export function lpUserOpReviewCommitment(operationId, context, gasReview) {
|
|
22
|
+
if (gasReview)
|
|
23
|
+
return sponsoredDigest({ domain: 'otto-account-gas-review-v1', operationId, context, gasReview });
|
|
24
|
+
return sponsoredDigest({ domain: 'otto-sponsored-lp-collect-review-v1', operationId, context });
|
|
25
|
+
}
|
|
26
|
+
function invalid() { throw new Error('SPONSOR_LP_JOURNAL_INVALID'); }
|
|
27
|
+
export function assertLpUserOpContext(context, fresh = false, nowMs = Date.now()) {
|
|
28
|
+
const envelope = assertLpUserOpEnvelope(context.envelope), terms = envelope.terms;
|
|
29
|
+
const chain = sponsoredChainTerms(context.activation, context.chainId), profile = chain.nativeYieldUserOpProfile;
|
|
30
|
+
if (context.account !== terms.account || context.chainId !== terms.chainId ||
|
|
31
|
+
context.authority.account !== context.account || context.authority.userId !== terms.userId ||
|
|
32
|
+
context.authority.activationDigest !== sponsoredDigest(context.activation) ||
|
|
33
|
+
context.authority.policyRulesDigest !== context.activation.policyRulesDigest ||
|
|
34
|
+
context.reviewedAtMs !== envelope.preparedAtMs || context.validUntilMs !== envelope.validUntilMs ||
|
|
35
|
+
context.validUntilMs > context.activation.expiresAtMs || context.validUntilMs > Date.parse(context.authority.grantExpiresAt) ||
|
|
36
|
+
hybridYieldCommitment(context.review.input) !== hybridYieldCommitment(envelope.input) ||
|
|
37
|
+
hybridYieldCommitment(context.review.terms) !== hybridYieldCommitment(terms) ||
|
|
38
|
+
!sponsoredActivationSupports(context.activation, 'lp-collect-userop') ||
|
|
39
|
+
terms.sponsoredActivationId !== context.activation.id || terms.sponsoredActivationDigest !== sponsoredDigest(context.activation) ||
|
|
40
|
+
terms.projectId !== context.activation.projectId || terms.policyId !== context.activation.policyId ||
|
|
41
|
+
terms.policyRulesDigest !== context.activation.policyRulesDigest ||
|
|
42
|
+
terms.providerEnforcementEvidenceDigest !== context.activation.providerEnforcementEvidenceDigest ||
|
|
43
|
+
context.activation.lpUserOpPolicyRelation?.lpActivationCommitment !== terms.lpActivationCommitment ||
|
|
44
|
+
context.activation.lpUserOpPolicyRelation?.compiledFullPolicyDigest !== terms.policyRulesDigest ||
|
|
45
|
+
!profile || terms.implementation !== profile.implementation || terms.implementationCodeHash !== profile.implementationCodeHash ||
|
|
46
|
+
terms.delegateBatchAbi !== profile.delegateBatchAbi || terms.delegateBatchReviewDigest !== profile.delegateBatchReviewDigest ||
|
|
47
|
+
terms.entryPointVersion !== profile.entryPointVersion || terms.delegate !== chain.delegate || terms.delegateCodeHash !== chain.delegateCodeHash ||
|
|
48
|
+
terms.entryPoint !== chain.entryPoint || terms.entryPointCodeHash !== chain.entryPointCodeHash ||
|
|
49
|
+
terms.paymaster !== chain.paymaster || terms.paymasterCodeHash !== chain.paymasterCodeHash ||
|
|
50
|
+
terms.sponsorshipPolicyId !== chain.sponsorshipPolicyId || terms.maxGasCostWei !== chain.maxGasCostWei ||
|
|
51
|
+
terms.maxUserGasCostWei !== chain.maxUserGasCostWei || terms.maxTotalGasCostWei !== chain.maxTotalGasCostWei)
|
|
52
|
+
invalid();
|
|
53
|
+
if (fresh && (context.activation.mode !== 'enabled' || context.activation.approvedAtMs > nowMs ||
|
|
54
|
+
nowMs < context.reviewedAtMs || nowMs >= context.validUntilMs))
|
|
55
|
+
throw new Error('SPONSOR_REVIEW_EXPIRED');
|
|
56
|
+
}
|
|
57
|
+
export function validateLpUserOpOperation(raw) {
|
|
58
|
+
const op = LP_USEROP_OPERATION_SCHEMA.parse(raw), context = op.context;
|
|
59
|
+
assertLpUserOpContext(context);
|
|
60
|
+
const { envelope } = context, { terms } = envelope;
|
|
61
|
+
if (op.userId !== context.authority.userId || op.reviewCommitment !== lpUserOpReviewCommitment(op.operationId, context, op.gasReview) ||
|
|
62
|
+
(op.state === 'review') !== !op.reviewConsumed || op.updatedAtMs < op.createdAtMs ||
|
|
63
|
+
context.reviewedAtMs !== op.createdAtMs || context.validUntilMs <= context.reviewedAtMs)
|
|
64
|
+
invalid();
|
|
65
|
+
if (op.request) {
|
|
66
|
+
const expectedBody = JSON.stringify({ network: SPONSORED_CDP_NETWORKS[context.chainId],
|
|
67
|
+
calls: lpUserOpRecordedCalls(envelope), useCdpPaymaster: false,
|
|
68
|
+
...(op.gasReview?.payer === 'account' ? {} : { paymasterAlias: `pimlico:${context.chainId}`,
|
|
69
|
+
paymasterContext: { sponsorshipPolicyId: terms.sponsorshipPolicyId } }) });
|
|
70
|
+
if (op.request.userId !== op.userId || op.request.account !== context.account || op.request.chainId !== context.chainId ||
|
|
71
|
+
op.request.idempotencyKey !== op.operationId.slice(3) ||
|
|
72
|
+
op.request.path !== `/platform/v2/embedded-wallet-api/end-users/${op.userId}/evm/smart-accounts/${context.account.toLowerCase()}/send` ||
|
|
73
|
+
op.request.body !== expectedBody)
|
|
74
|
+
invalid();
|
|
75
|
+
}
|
|
76
|
+
if (op.handoffAttempted && !op.request)
|
|
77
|
+
invalid();
|
|
78
|
+
if (op.state === 'review' && (op.request || op.handoffAttempted || op.submitEvidence || op.userOpHash || op.transactionHash))
|
|
79
|
+
invalid();
|
|
80
|
+
if ((op.userOpHash || op.transactionHash || op.receipt) && !op.handoffAttempted)
|
|
81
|
+
invalid();
|
|
82
|
+
if (op.userOpHash && !op.submitEvidence)
|
|
83
|
+
invalid();
|
|
84
|
+
if (op.transactionHash && !op.userOpHash)
|
|
85
|
+
invalid();
|
|
86
|
+
if (op.submitEvidence) {
|
|
87
|
+
const { baseline, check, verifiedAtMs } = op.submitEvidence;
|
|
88
|
+
const { checkCommitment, ...checkCore } = check;
|
|
89
|
+
if (checkCommitment !== protocolCommitment(checkCore) || verifiedAtMs > op.updatedAtMs ||
|
|
90
|
+
verifiedAtMs < envelope.preparedAtMs || verifiedAtMs >= context.validUntilMs ||
|
|
91
|
+
check.checkedAt !== Math.floor(verifiedAtMs / 1000) || check.planCommitment !== envelope.plan.planCommitment ||
|
|
92
|
+
check.intentCommitment !== terms.intentCommitment || check.policyCommitment !== envelope.plan.policyCommitment ||
|
|
93
|
+
check.baselineCommitment !== protocolCommitment(baseline) || baseline.account !== context.account.toLowerCase() ||
|
|
94
|
+
BigInt(baseline.blockNumber) < BigInt(envelope.baseline.blockNumber) || baseline.position.approved !== terms.initialNftApproval)
|
|
95
|
+
invalid();
|
|
96
|
+
}
|
|
97
|
+
if (op.receipt && (op.receipt.userOpHash.toLowerCase() !== op.userOpHash?.toLowerCase() ||
|
|
98
|
+
op.receipt.transactionHash.toLowerCase() !== op.transactionHash?.toLowerCase() || op.receipt.chainId !== context.chainId ||
|
|
99
|
+
op.receipt.account !== context.account || op.receipt.paymaster !== (op.gasReview?.payer === 'account' ? '0x0000000000000000000000000000000000000000' : terms.paymaster) ||
|
|
100
|
+
(op.gasReview?.payer !== 'account' && BigInt(op.receipt.actualGasCostWei) > BigInt(terms.maxGasCostWei))))
|
|
101
|
+
invalid();
|
|
102
|
+
if (op.gasCapBreach && (op.gasReview?.payer === 'account' || !op.handoffAttempted || op.state !== 'submitted' || op.receipt || op.settlement ||
|
|
103
|
+
op.failureCode !== 'SPONSOR_GAS_CAP_EXCEEDED' || op.gasCapBreach.userOpHash.toLowerCase() !== op.userOpHash?.toLowerCase() ||
|
|
104
|
+
op.gasCapBreach.transactionHash.toLowerCase() !== op.transactionHash?.toLowerCase() ||
|
|
105
|
+
op.gasCapBreach.chainId !== context.chainId || op.gasCapBreach.account !== context.account ||
|
|
106
|
+
op.gasCapBreach.paymaster !== terms.paymaster || BigInt(op.gasCapBreach.actualGasCostWei) <= BigInt(terms.maxGasCostWei)))
|
|
107
|
+
invalid();
|
|
108
|
+
const result = op.settlement;
|
|
109
|
+
if (result) {
|
|
110
|
+
if (!result.releaseClaim)
|
|
111
|
+
return invalid();
|
|
112
|
+
const receipt = op.receipt;
|
|
113
|
+
if (!receipt || !op.submitEvidence || result.userOpHash !== op.userOpHash || result.transactionHash !== op.transactionHash ||
|
|
114
|
+
result.blockHash !== receipt.blockHash || result.blockNumber !== receipt.blockNumber || result.userOpNonce !== receipt.nonce ||
|
|
115
|
+
result.owner !== context.account || result.chainId !== context.chainId || result.pool !== envelope.input.pool ||
|
|
116
|
+
result.tokenId !== envelope.input.tokenId || result.planCommitment !== envelope.plan.planCommitment ||
|
|
117
|
+
result.baselineCommitment !== op.submitEvidence.check.baselineCommitment ||
|
|
118
|
+
result.actualGasCostWei !== receipt.actualGasCostWei || result.actualGasUsed !== receipt.actualGasUsed ||
|
|
119
|
+
result.paymaster !== receipt.paymaster || result.finality.policyCommitment !== protocolCommitment(envelope.finalityPolicy) ||
|
|
120
|
+
result.finality.observedAtMs < op.submitEvidence.verifiedAtMs ||
|
|
121
|
+
BigInt(result.finality.finalizedBlockNumber) < BigInt(result.blockNumber) ||
|
|
122
|
+
BigInt(result.finality.confirmations) !== BigInt(result.finality.finalizedBlockNumber) - BigInt(result.blockNumber) + 1n ||
|
|
123
|
+
BigInt(result.finality.confirmations) < BigInt(envelope.finalityPolicy.minFinalizedConfirmations))
|
|
124
|
+
invalid();
|
|
125
|
+
if (result.status === 'completed') {
|
|
126
|
+
if (op.state !== 'completed' || receipt.status !== 'success' || result.collectedRaw.some((amount, i) => BigInt(amount) > BigInt(envelope.input.amountMax[i]) || BigInt(amount) < BigInt(envelope.input.amountMin[i])))
|
|
127
|
+
invalid();
|
|
128
|
+
}
|
|
129
|
+
else if (op.state !== 'failed' || receipt.status !== 'reverted' || result.collectedRaw.some(amount => amount !== '0'))
|
|
130
|
+
invalid();
|
|
131
|
+
}
|
|
132
|
+
if (op.state === 'completed' && result?.status !== 'completed')
|
|
133
|
+
invalid();
|
|
134
|
+
if (op.state === 'failed' && op.handoffAttempted && result?.status !== 'reverted')
|
|
135
|
+
invalid();
|
|
136
|
+
if (op.state === 'submitted' && !op.userOpHash)
|
|
137
|
+
invalid();
|
|
138
|
+
return op;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=lp-userop-operation.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Header authorization precedes every input parse and optional LP runtime access. */
|
|
2
|
+
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import type { ExecutionToolRuntime } from './execution-tools.js';
|
|
4
|
+
import { type LpUserOpAvailabilityResult } from './lp-userop-mcp-definition.js';
|
|
5
|
+
export interface LpUserOpBinding {
|
|
6
|
+
availability(): Promise<LpUserOpAvailabilityResult>;
|
|
7
|
+
reviewGas(input: unknown): Promise<unknown>;
|
|
8
|
+
prepare(input: unknown): Promise<unknown>;
|
|
9
|
+
submit(input: unknown): Promise<unknown>;
|
|
10
|
+
read(input: unknown): Promise<unknown>;
|
|
11
|
+
reconcile(input: unknown): Promise<unknown>;
|
|
12
|
+
}
|
|
13
|
+
export declare function unavailableLpUserOpBinding(reason?: NonNullable<LpUserOpAvailabilityResult['reason']>): LpUserOpBinding;
|
|
14
|
+
export declare function callLpUserOp(runtime: ExecutionToolRuntime, raw: unknown, headers: Readonly<Record<string, string | string[] | undefined>> | undefined): Promise<CallToolResult>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { accountGasFailure } from './execution-account-gas-view.js';
|
|
2
|
+
import { LpUserOpPreHandoffRefusal } from './lp-userop-account.js';
|
|
3
|
+
import { ZodError } from 'zod';
|
|
4
|
+
import { assertDelegationCaller, callerFromHeaders } from './execution-delegation.js';
|
|
5
|
+
import { ExecutionRefusal, isExecutionRefusal } from './execution-errors.js';
|
|
6
|
+
import { SponsoredAccountRefusal } from './execution-sponsored-runtime.js';
|
|
7
|
+
import { lpUserOpAvailability } from './lp-userop-index.js';
|
|
8
|
+
import { LP_USEROP_ACTION_INPUT, LP_USEROP_TOOL_RESULT, LP_USEROP_AVAILABILITY_RESULT } from './lp-userop-mcp-definition.js';
|
|
9
|
+
export function unavailableLpUserOpBinding(reason = 'SPONSOR_ACTIVATION_UNCONFIGURED') {
|
|
10
|
+
const refuse = async () => { throw new SponsoredAccountRefusal(reason); };
|
|
11
|
+
return Object.freeze({ availability: async () => LP_USEROP_AVAILABILITY_RESULT.parse({ ...lpUserOpAvailability(),
|
|
12
|
+
executionEnabled: false, recoveryAvailable: false, reason }),
|
|
13
|
+
reviewGas: refuse, prepare: refuse, submit: refuse, read: refuse, reconcile: refuse });
|
|
14
|
+
}
|
|
15
|
+
function success(raw) {
|
|
16
|
+
const output = LP_USEROP_TOOL_RESULT.parse(raw);
|
|
17
|
+
return { content: [{ type: 'text', text: JSON.stringify(output) }], structuredContent: output };
|
|
18
|
+
}
|
|
19
|
+
// Factory refusals occur before the service can start this invocation. Arbitrary errors remain unknown.
|
|
20
|
+
const BEFORE_HANDOFF_REFUSALS = new Set(['SPONSOR_ACTIVATION_UNCONFIGURED', 'SPONSOR_ACTIVATION_UNAVAILABLE',
|
|
21
|
+
'SPONSOR_CAPABILITY_UNAVAILABLE', 'SPONSOR_RECOVERY_ONLY', 'SPONSOR_RUNTIME_UNAVAILABLE',
|
|
22
|
+
'SPONSOR_JOURNAL_UNAVAILABLE', 'SPONSOR_CAP_UNAVAILABLE', 'SPONSOR_CHAIN_UNAVAILABLE']);
|
|
23
|
+
export async function callLpUserOp(runtime, raw, headers) {
|
|
24
|
+
let submitId, invoked = false;
|
|
25
|
+
try {
|
|
26
|
+
if (!runtime.delegation)
|
|
27
|
+
throw new ExecutionRefusal('DELEGATION_UNAUTHORIZED', 'Protected account caller is not authorized');
|
|
28
|
+
assertDelegationCaller(runtime.delegation, callerFromHeaders(headers));
|
|
29
|
+
const input = LP_USEROP_ACTION_INPUT.parse(raw), binding = runtime.lpUserOp ?? unavailableLpUserOpBinding();
|
|
30
|
+
if (input.action === 'availability')
|
|
31
|
+
return success(LP_USEROP_AVAILABILITY_RESULT.parse(await binding.availability()));
|
|
32
|
+
const { action, ...body } = input;
|
|
33
|
+
if (action === 'submit')
|
|
34
|
+
submitId = input.operationId;
|
|
35
|
+
invoked = true;
|
|
36
|
+
const result = LP_USEROP_TOOL_RESULT.parse(await binding[action](body));
|
|
37
|
+
if (!('state' in result) || !('operationId' in result))
|
|
38
|
+
throw new Error('mismatched operation');
|
|
39
|
+
if (action === 'prepare') {
|
|
40
|
+
if (result.state !== 'review' || result.account.toLowerCase() !== input.input.account ||
|
|
41
|
+
result.chainId !== input.input.chainId || result.pool !== input.input.pool || result.tokenId !== input.input.tokenId ||
|
|
42
|
+
JSON.stringify(result.collection.maximum) !== JSON.stringify(input.input.amountMax) ||
|
|
43
|
+
JSON.stringify(result.collection.minimum) !== JSON.stringify(input.input.amountMin))
|
|
44
|
+
throw new Error('mismatched preparation');
|
|
45
|
+
}
|
|
46
|
+
else if (result.operationId !== input.operationId)
|
|
47
|
+
throw new Error('mismatched operation');
|
|
48
|
+
if (input.action === 'reviewGas' && (result.state !== 'review' || result.reviewConsumed || result.gas.reviewRequired !== true || !result.gas.paymentReview ||
|
|
49
|
+
result.reviewCommitment === input.reviewCommitment || (input.preference !== 'auto' && result.gas.payer !== input.preference)))
|
|
50
|
+
throw new Error('mismatched gas review');
|
|
51
|
+
if (action === 'submit' && (!result.reviewConsumed || result.envelopeCommitment !== input.envelopeCommitment ||
|
|
52
|
+
result.reviewCommitment !== input.reviewCommitment))
|
|
53
|
+
throw new Error('mismatched consumed review');
|
|
54
|
+
return success(result);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const knownRefusal = error instanceof LpUserOpPreHandoffRefusal ||
|
|
58
|
+
(error instanceof SponsoredAccountRefusal && BEFORE_HANDOFF_REFUSALS.has(error.code));
|
|
59
|
+
if (submitId && invoked && !knownRefusal)
|
|
60
|
+
return success({ schemaVersion: '1', kind: 'lp-collect-userop', status: 'outcome-unknown', operationId: submitId,
|
|
61
|
+
requiresReconciliation: true, replacementPermitted: false,
|
|
62
|
+
attention: 'The outcome of the original submission is unknown. Read or reconcile this operation; do not submit a replacement.' });
|
|
63
|
+
const denied = isExecutionRefusal(error) && error.code === 'DELEGATION_UNAUTHORIZED' && !invoked;
|
|
64
|
+
const code = denied ? 'DELEGATION_UNAUTHORIZED' : error instanceof ZodError && !invoked
|
|
65
|
+
? 'LP_USEROP_REQUEST_INVALID' : accountGasFailure(error) ?? 'LP_USEROP_REQUEST_REFUSED';
|
|
66
|
+
return { isError: true, content: [{ type: 'text', text: JSON.stringify({ status: 'refused', code,
|
|
67
|
+
message: denied ? 'The protected account caller is not authorized.' : 'The LP collection action is unavailable or refused. Read current availability and the original operation before continuing.' }) }] };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=lp-userop-registration.js.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** Canonical EntryPoint transaction and isolated LP accounting proof. Never sends or retries. */
|
|
2
|
+
import { type Hex } from 'viem';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { type LpUserOpEnvelope, type LpUserOpRuntime, type LpUserOpSubmitEvidence } from './lp-userop.js';
|
|
5
|
+
export declare const LP_USEROP_SETTLEMENT_SCHEMA: z.ZodUnion<readonly [z.ZodObject<{
|
|
6
|
+
status: z.ZodEnum<{
|
|
7
|
+
unknown: "unknown";
|
|
8
|
+
held: "held";
|
|
9
|
+
}>;
|
|
10
|
+
reason: z.ZodString;
|
|
11
|
+
userOpHash: z.ZodString;
|
|
12
|
+
transactionHash: z.ZodString;
|
|
13
|
+
releaseClaim: z.ZodLiteral<false>;
|
|
14
|
+
retrySafe: z.ZodLiteral<false>;
|
|
15
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
16
|
+
userOpHash: z.ZodString;
|
|
17
|
+
transactionHash: z.ZodString;
|
|
18
|
+
blockHash: z.ZodString;
|
|
19
|
+
blockNumber: z.ZodString;
|
|
20
|
+
userOpNonce: z.ZodString;
|
|
21
|
+
owner: z.ZodString;
|
|
22
|
+
chainId: z.ZodLiteral<8453>;
|
|
23
|
+
pool: z.ZodString;
|
|
24
|
+
tokenId: z.ZodString;
|
|
25
|
+
planCommitment: z.ZodString;
|
|
26
|
+
baselineCommitment: z.ZodString;
|
|
27
|
+
accountingCommitment: z.ZodString;
|
|
28
|
+
collectedRaw: z.ZodTuple<[z.ZodString, z.ZodString], null>;
|
|
29
|
+
liquidityDelta: z.ZodLiteral<"0">;
|
|
30
|
+
ownerBalancesAtBlock: z.ZodTuple<[z.ZodString, z.ZodString], null>;
|
|
31
|
+
nftTokenApprovalCleared: z.ZodLiteral<true>;
|
|
32
|
+
erc20AllowancesCleared: z.ZodLiteral<true>;
|
|
33
|
+
actualGasCostWei: z.ZodString;
|
|
34
|
+
actualGasUsed: z.ZodString;
|
|
35
|
+
paymaster: z.ZodString;
|
|
36
|
+
profitClassification: z.ZodLiteral<"UNKNOWN">;
|
|
37
|
+
distributableProfitRaw: z.ZodNull;
|
|
38
|
+
releaseClaim: z.ZodLiteral<true>;
|
|
39
|
+
retrySafe: z.ZodLiteral<false>;
|
|
40
|
+
finality: z.ZodObject<{
|
|
41
|
+
policyCommitment: z.ZodString;
|
|
42
|
+
observedAtMs: z.ZodNumber;
|
|
43
|
+
finalizedBlockNumber: z.ZodString;
|
|
44
|
+
finalizedBlockHash: z.ZodString;
|
|
45
|
+
confirmations: z.ZodString;
|
|
46
|
+
}, z.core.$strict>;
|
|
47
|
+
status: z.ZodLiteral<"completed">;
|
|
48
|
+
protocolActionVerified: z.ZodLiteral<true>;
|
|
49
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
50
|
+
userOpHash: z.ZodString;
|
|
51
|
+
transactionHash: z.ZodString;
|
|
52
|
+
blockHash: z.ZodString;
|
|
53
|
+
blockNumber: z.ZodString;
|
|
54
|
+
userOpNonce: z.ZodString;
|
|
55
|
+
owner: z.ZodString;
|
|
56
|
+
chainId: z.ZodLiteral<8453>;
|
|
57
|
+
pool: z.ZodString;
|
|
58
|
+
tokenId: z.ZodString;
|
|
59
|
+
planCommitment: z.ZodString;
|
|
60
|
+
baselineCommitment: z.ZodString;
|
|
61
|
+
accountingCommitment: z.ZodString;
|
|
62
|
+
collectedRaw: z.ZodTuple<[z.ZodString, z.ZodString], null>;
|
|
63
|
+
liquidityDelta: z.ZodLiteral<"0">;
|
|
64
|
+
ownerBalancesAtBlock: z.ZodTuple<[z.ZodString, z.ZodString], null>;
|
|
65
|
+
nftTokenApprovalCleared: z.ZodLiteral<true>;
|
|
66
|
+
erc20AllowancesCleared: z.ZodLiteral<true>;
|
|
67
|
+
actualGasCostWei: z.ZodString;
|
|
68
|
+
actualGasUsed: z.ZodString;
|
|
69
|
+
paymaster: z.ZodString;
|
|
70
|
+
profitClassification: z.ZodLiteral<"UNKNOWN">;
|
|
71
|
+
distributableProfitRaw: z.ZodNull;
|
|
72
|
+
releaseClaim: z.ZodLiteral<true>;
|
|
73
|
+
retrySafe: z.ZodLiteral<false>;
|
|
74
|
+
finality: z.ZodObject<{
|
|
75
|
+
policyCommitment: z.ZodString;
|
|
76
|
+
observedAtMs: z.ZodNumber;
|
|
77
|
+
finalizedBlockNumber: z.ZodString;
|
|
78
|
+
finalizedBlockHash: z.ZodString;
|
|
79
|
+
confirmations: z.ZodString;
|
|
80
|
+
}, z.core.$strict>;
|
|
81
|
+
status: z.ZodLiteral<"reverted">;
|
|
82
|
+
protocolActionVerified: z.ZodLiteral<false>;
|
|
83
|
+
}, z.core.$strict>]>;
|
|
84
|
+
export type LpUserOpSettlement = z.infer<typeof LP_USEROP_SETTLEMENT_SCHEMA>;
|
|
85
|
+
export interface LpUserOpReceiptBinding {
|
|
86
|
+
readonly userOpHash: Hex;
|
|
87
|
+
readonly transactionHash: Hex;
|
|
88
|
+
}
|
|
89
|
+
export declare function readLpUserOpSettlement(rawEnvelope: LpUserOpEnvelope, binding: LpUserOpReceiptBinding, runtime: LpUserOpRuntime, rawSubmitEvidence: LpUserOpSubmitEvidence, payer?: 'sponsor' | 'account'): Promise<LpUserOpSettlement>;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/** Canonical EntryPoint transaction and isolated LP accounting proof. Never sends or retries. */
|
|
2
|
+
import { decodeEventLog, decodeFunctionData, encodeFunctionData, keccak256, parseAbi, recoverTransactionAddress, serializeTransaction } from 'viem';
|
|
3
|
+
import { getUserOperationHash } from 'viem/account-abstraction';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { freezeHybridValue } from './hybrid-yield-markets.js';
|
|
6
|
+
import { readConfirmedSponsoredReceipt, SponsoredReceiptUnconfirmedError } from './execution-sponsored-receipt.js';
|
|
7
|
+
import { sponsoredOperationLogs } from './execution-sponsored-settlement.js';
|
|
8
|
+
import { commitment as protocolCommitment } from './vendor/lp-v3/lp-v3-contract.js';
|
|
9
|
+
import { lpV3CollectProtocolEvidenceSchema } from './vendor/lp-v3/lp-v3-collect-protocol-contract.js';
|
|
10
|
+
import { assertLpUserOpEnvelope, assertLpUserOpAccountState, boundedLpUserOpRead, lpUserOpBatchData, lpUserOpNow, lpUserOpProtocol, lpUserOpProtocolClient, requireLpUserOp, LP_USEROP_SUBMIT_EVIDENCE_SCHEMA, LpUserOpError } from './lp-userop.js';
|
|
11
|
+
const EP_ABI = parseAbi([
|
|
12
|
+
'function handleOps((address sender,uint256 nonce,bytes initCode,bytes callData,uint256 callGasLimit,uint256 verificationGasLimit,uint256 preVerificationGas,uint256 maxFeePerGas,uint256 maxPriorityFeePerGas,bytes paymasterAndData,bytes signature)[] ops,address beneficiary)',
|
|
13
|
+
'event UserOperationEvent(bytes32 indexed userOpHash,address indexed sender,address indexed paymaster,uint256 nonce,bool success,uint256 actualGasCost,uint256 actualGasUsed)',
|
|
14
|
+
'event BeforeExecution()',
|
|
15
|
+
]);
|
|
16
|
+
const hash = z.string().regex(/^0x[0-9a-fA-F]{64}$/), uint = z.string().regex(/^(0|[1-9][0-9]*)$/);
|
|
17
|
+
const held = z.object({ status: z.enum(['held', 'unknown']), reason: z.string(), userOpHash: hash, transactionHash: hash,
|
|
18
|
+
releaseClaim: z.literal(false), retrySafe: z.literal(false) }).strict();
|
|
19
|
+
const accounted = z.object({
|
|
20
|
+
userOpHash: hash, transactionHash: hash, blockHash: hash, blockNumber: uint, userOpNonce: uint,
|
|
21
|
+
owner: z.string(), chainId: z.literal(8453), pool: z.string(), tokenId: uint, planCommitment: hash,
|
|
22
|
+
baselineCommitment: hash, accountingCommitment: hash, collectedRaw: z.tuple([uint, uint]), liquidityDelta: z.literal('0'),
|
|
23
|
+
ownerBalancesAtBlock: z.tuple([uint, uint]), nftTokenApprovalCleared: z.literal(true), erc20AllowancesCleared: z.literal(true),
|
|
24
|
+
actualGasCostWei: uint, actualGasUsed: uint, paymaster: z.string(),
|
|
25
|
+
profitClassification: z.literal('UNKNOWN'), distributableProfitRaw: z.null(),
|
|
26
|
+
releaseClaim: z.literal(true), retrySafe: z.literal(false),
|
|
27
|
+
finality: z.object({ policyCommitment: hash, observedAtMs: z.number().int().positive().safe(),
|
|
28
|
+
finalizedBlockNumber: uint, finalizedBlockHash: hash, confirmations: uint }).strict(),
|
|
29
|
+
});
|
|
30
|
+
export const LP_USEROP_SETTLEMENT_SCHEMA = z.union([held,
|
|
31
|
+
accounted.extend({ status: z.literal('completed'), protocolActionVerified: z.literal(true) }).strict(),
|
|
32
|
+
accounted.extend({ status: z.literal('reverted'), protocolActionVerified: z.literal(false) }).strict(),
|
|
33
|
+
]);
|
|
34
|
+
const same = (a, b) => typeof a === 'string' && a.toLowerCase() === b.toLowerCase();
|
|
35
|
+
async function proveTransaction(runtime, envelope, binding, receipt, nonce, payer) {
|
|
36
|
+
const tx = await runtime.client.getTransaction({ hash: binding.transactionHash }), terms = envelope.terms;
|
|
37
|
+
requireLpUserOp(tx.type === 'eip1559' && tx.chainId === terms.chainId && same(tx.hash, binding.transactionHash) &&
|
|
38
|
+
same(tx.to, terms.entryPoint) && tx.value === 0n && same(tx.blockHash, receipt.blockHash) && tx.blockNumber === receipt.blockNumber &&
|
|
39
|
+
tx.transactionIndex === receipt.transactionIndex && same(tx.from, receipt.from) && same(receipt.to, terms.entryPoint) &&
|
|
40
|
+
Number.isSafeInteger(tx.nonce) && tx.nonce >= 0 && tx.gas > 0n && tx.maxFeePerGas > 0n &&
|
|
41
|
+
tx.maxPriorityFeePerGas >= 0n && tx.maxPriorityFeePerGas <= tx.maxFeePerGas &&
|
|
42
|
+
(tx.yParity === 0 || tx.yParity === 1) && BigInt(tx.r) > 0n &&
|
|
43
|
+
BigInt(tx.r) < 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n &&
|
|
44
|
+
BigInt(tx.s) > 0n && BigInt(tx.s) <= 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0n, 'LP_USEROP_TRANSACTION_BINDING');
|
|
45
|
+
const serialized = serializeTransaction({ type: 'eip1559', chainId: tx.chainId, nonce: tx.nonce, gas: tx.gas,
|
|
46
|
+
maxFeePerGas: tx.maxFeePerGas, maxPriorityFeePerGas: tx.maxPriorityFeePerGas, to: tx.to, value: tx.value,
|
|
47
|
+
data: tx.input, accessList: tx.accessList }, { r: tx.r, s: tx.s, yParity: tx.yParity });
|
|
48
|
+
requireLpUserOp(same(keccak256(serialized), binding.transactionHash) &&
|
|
49
|
+
same(await recoverTransactionAddress({ serializedTransaction: serialized }), tx.from), 'LP_USEROP_TRANSACTION_SIGNATURE');
|
|
50
|
+
const decoded = decodeFunctionData({ abi: EP_ABI, data: tx.input });
|
|
51
|
+
requireLpUserOp(decoded.functionName === 'handleOps' && decoded.args[0].length > 0 && decoded.args[0].length <= 256 &&
|
|
52
|
+
encodeFunctionData({ abi: EP_ABI, functionName: 'handleOps', args: decoded.args }) === tx.input, 'LP_USEROP_UNSUPPORTED_BUNDLE');
|
|
53
|
+
const operations = decoded.args[0].map(operation => ({ operation, hash: getUserOperationHash({ chainId: terms.chainId,
|
|
54
|
+
entryPointAddress: terms.entryPoint, entryPointVersion: '0.6', userOperation: operation }) }));
|
|
55
|
+
const matching = operations.filter(operation => same(operation.hash, binding.userOpHash));
|
|
56
|
+
requireLpUserOp(matching.length === 1 && operations.filter(({ operation }) => same(operation.sender, terms.account)).length === 1, 'LP_USEROP_HASH');
|
|
57
|
+
const operation = matching[0].operation;
|
|
58
|
+
requireLpUserOp(same(operation.sender, terms.account) && operation.nonce.toString() === nonce &&
|
|
59
|
+
operation.initCode === '0x' && same(operation.callData, lpUserOpBatchData(envelope)) &&
|
|
60
|
+
(payer === 'account' ? operation.paymasterAndData === '0x' :
|
|
61
|
+
operation.paymasterAndData.length >= 42 && same(operation.paymasterAndData.slice(0, 42), terms.paymaster)) &&
|
|
62
|
+
operation.callGasLimit > 0n && operation.verificationGasLimit > 0n && operation.preVerificationGas > 0n &&
|
|
63
|
+
operation.maxFeePerGas > 0n && operation.maxPriorityFeePerGas <= operation.maxFeePerGas, 'LP_USEROP_CALLDATA');
|
|
64
|
+
const events = [];
|
|
65
|
+
let beforeExecution = -1;
|
|
66
|
+
for (const log of receipt.logs) {
|
|
67
|
+
if (!same(log.address, terms.entryPoint))
|
|
68
|
+
continue;
|
|
69
|
+
let event;
|
|
70
|
+
try {
|
|
71
|
+
event = decodeEventLog({ abi: EP_ABI, data: log.data, topics: log.topics, strict: true });
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (event.eventName === 'BeforeExecution') {
|
|
77
|
+
requireLpUserOp(beforeExecution === -1 && events.length === 0, 'LP_USEROP_BUNDLE_BOUNDARY');
|
|
78
|
+
beforeExecution = log.logIndex;
|
|
79
|
+
}
|
|
80
|
+
else if (event.eventName === 'UserOperationEvent') {
|
|
81
|
+
requireLpUserOp(beforeExecution >= 0, 'LP_USEROP_BUNDLE_BOUNDARY');
|
|
82
|
+
events.push({ hash: event.args.userOpHash, sender: event.args.sender, nonce: event.args.nonce, index: log.logIndex });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
requireLpUserOp(events.length === operations.length && events.every((event, i) => same(event.hash, operations[i].hash) &&
|
|
86
|
+
same(event.sender, operations[i].operation.sender) && event.nonce === operations[i].operation.nonce), 'LP_USEROP_BUNDLE_EVENTS');
|
|
87
|
+
const eventIndex = events.findIndex(event => same(event.hash, binding.userOpHash));
|
|
88
|
+
requireLpUserOp(eventIndex >= 0, 'LP_USEROP_BUNDLE_EVENTS');
|
|
89
|
+
return { windowStartLogIndex: eventIndex === 0 ? beforeExecution : events[eventIndex - 1].index,
|
|
90
|
+
userOpEventLogIndex: events[eventIndex].index };
|
|
91
|
+
}
|
|
92
|
+
export async function readLpUserOpSettlement(rawEnvelope, binding, runtime, rawSubmitEvidence, payer = 'sponsor') {
|
|
93
|
+
const retain = (reason, status = 'held') => ({ status, reason, ...binding, releaseClaim: false, retrySafe: false });
|
|
94
|
+
try {
|
|
95
|
+
return await boundedLpUserOpRead(runtime, async () => {
|
|
96
|
+
const envelope = assertLpUserOpEnvelope(rawEnvelope), submit = LP_USEROP_SUBMIT_EVIDENCE_SCHEMA.parse(rawSubmitEvidence);
|
|
97
|
+
const { plan, input, terms, finalityPolicy: policy } = envelope, protocol = lpUserOpProtocol(runtime, envelope);
|
|
98
|
+
requireLpUserOp(submit.verifiedAtMs >= envelope.preparedAtMs && submit.verifiedAtMs < envelope.validUntilMs &&
|
|
99
|
+
submit.check.checkedAt === Math.floor(submit.verifiedAtMs / 1000), 'LP_USEROP_SUBMIT_BASELINE');
|
|
100
|
+
const checked = protocol.validatePrepared(plan, input, submit.baseline, { now: () => submit.check.checkedAt });
|
|
101
|
+
requireLpUserOp(protocolCommitment(checked) === protocolCommitment(submit.check), 'LP_USEROP_SUBMIT_CHECK');
|
|
102
|
+
// Seal the finalized boundary before reading either transaction or historical account state.
|
|
103
|
+
const [finalized, latest] = await Promise.all([runtime.client.getBlock({ blockTag: 'finalized' }), runtime.client.getBlock({ blockTag: 'latest' })]);
|
|
104
|
+
const observedAtMs = lpUserOpNow(runtime), now = Math.floor(observedAtMs / 1000);
|
|
105
|
+
requireLpUserOp(finalized.number !== null && finalized.hash && latest.number !== null && latest.hash &&
|
|
106
|
+
finalized.number <= latest.number && finalized.timestamp <= latest.timestamp &&
|
|
107
|
+
Number(latest.timestamp) <= now && now - Number(latest.timestamp) <= policy.maxHeadAgeSeconds &&
|
|
108
|
+
now - Number(finalized.timestamp) <= policy.maxFinalizedLagSeconds, 'LP_USEROP_FINALITY_STALE');
|
|
109
|
+
const confirmed = await readConfirmedSponsoredReceipt({ ...binding, chainId: terms.chainId, account: terms.account,
|
|
110
|
+
payer, entryPoint: terms.entryPoint, paymaster: payer === 'account' ? '0x0000000000000000000000000000000000000000' : terms.paymaster, maxGasCostWei: BigInt(terms.maxGasCostWei) }, runtime.client);
|
|
111
|
+
const receipt = await runtime.client.getTransactionReceipt({ hash: binding.transactionHash });
|
|
112
|
+
requireLpUserOp(receipt.transactionHash === confirmed.transactionHash && receipt.blockHash === confirmed.blockHash &&
|
|
113
|
+
receipt.blockNumber.toString() === confirmed.blockNumber && receipt.type === 'eip1559' &&
|
|
114
|
+
Number.isSafeInteger(receipt.transactionIndex) && receipt.transactionIndex >= 0 &&
|
|
115
|
+
receipt.gasUsed > 0n && receipt.effectiveGasPrice > 0n && receipt.cumulativeGasUsed >= receipt.gasUsed &&
|
|
116
|
+
receipt.logs.length > 0 && receipt.logs.length <= 4096 && receipt.logs.every((log, i) => !log.removed &&
|
|
117
|
+
log.transactionHash === receipt.transactionHash && log.transactionIndex === receipt.transactionIndex && log.blockHash === receipt.blockHash &&
|
|
118
|
+
log.blockNumber === receipt.blockNumber && Number.isSafeInteger(log.logIndex) && log.logIndex >= 0 &&
|
|
119
|
+
(i === 0 || log.logIndex === receipt.logs[i - 1].logIndex + 1)), 'LP_USEROP_RECEIPT_BINDING');
|
|
120
|
+
const confirmations = finalized.number - receipt.blockNumber + 1n;
|
|
121
|
+
requireLpUserOp(confirmations >= BigInt(policy.minFinalizedConfirmations), 'LP_USEROP_NOT_FINALIZED');
|
|
122
|
+
const block = await runtime.client.getBlock({ blockNumber: receipt.blockNumber });
|
|
123
|
+
requireLpUserOp(block.number === receipt.blockNumber && block.hash === receipt.blockHash &&
|
|
124
|
+
receipt.blockNumber > BigInt(submit.baseline.blockNumber) && block.timestamp <= finalized.timestamp &&
|
|
125
|
+
Number(block.timestamp) > submit.baseline.blockTimestamp &&
|
|
126
|
+
(confirmed.status === 'reverted' || Number(block.timestamp) <= input.deadline) && same(typeof block.transactions[receipt.transactionIndex] === 'string'
|
|
127
|
+
? block.transactions[receipt.transactionIndex] : undefined, binding.transactionHash), 'LP_USEROP_BLOCK_BINDING');
|
|
128
|
+
const bounds = await proveTransaction(runtime, envelope, binding, receipt, confirmed.nonce, payer);
|
|
129
|
+
const historical = await protocol.collectProtocolEvidence(input, { client: lpUserOpProtocolClient(runtime.client),
|
|
130
|
+
nowSeconds: () => Math.floor(lpUserOpNow(runtime) / 1000), maxBlockAgeSeconds: policy.maxHeadAgeSeconds,
|
|
131
|
+
maxReadDurationSeconds: policy.maxReadDurationSeconds,
|
|
132
|
+
historicalAnchor: { number: submit.baseline.blockNumber, hash: submit.baseline.blockHash, timestamp: submit.baseline.blockTimestamp } });
|
|
133
|
+
// The new historical read has a CURRENT observation time. All chain fields must match the original
|
|
134
|
+
// submission baseline exactly; the persisted check continues to bind its original observation time.
|
|
135
|
+
const { observedAt: _newReadTime, ...historicalState } = historical;
|
|
136
|
+
const { observedAt: _originalReadTime, ...originalState } = submit.baseline;
|
|
137
|
+
requireLpUserOp(protocolCommitment(historicalState) === protocolCommitment(originalState), 'LP_USEROP_BASELINE_CHANGED');
|
|
138
|
+
await assertLpUserOpAccountState(runtime, terms, BigInt(submit.baseline.blockNumber), submit.baseline.blockHash);
|
|
139
|
+
await assertLpUserOpAccountState(runtime, terms, receipt.blockNumber, receipt.blockHash);
|
|
140
|
+
const post = await protocol.collectProtocolEvidence(input, { client: lpUserOpProtocolClient(runtime.client), nowSeconds: () => Math.floor(lpUserOpNow(runtime) / 1000),
|
|
141
|
+
maxBlockAgeSeconds: policy.maxHeadAgeSeconds, maxReadDurationSeconds: policy.maxReadDurationSeconds,
|
|
142
|
+
historicalAnchor: { number: receipt.blockNumber.toString(), hash: receipt.blockHash, timestamp: Number(block.timestamp) } });
|
|
143
|
+
const logs = sponsoredOperationLogs(receipt, terms.entryPoint, binding.userOpHash);
|
|
144
|
+
const evidence = lpV3CollectProtocolEvidenceSchema.parse({ schemaVersion: 3, planCommitment: plan.planCommitment,
|
|
145
|
+
status: confirmed.status, binding: { ...binding, ...bounds, chainId: terms.chainId, account: terms.account.toLowerCase(),
|
|
146
|
+
entryPoint: terms.entryPoint.toLowerCase(), blockNumber: receipt.blockNumber.toString(), blockHash: receipt.blockHash,
|
|
147
|
+
blockTimestamp: Number(block.timestamp), transactionIndex: receipt.transactionIndex, userOpNonce: confirmed.nonce },
|
|
148
|
+
logs: logs.map(log => ({ address: log.address.toLowerCase(), topics: log.topics, data: log.data, logIndex: log.logIndex,
|
|
149
|
+
transactionHash: log.transactionHash, transactionIndex: log.transactionIndex, blockHash: log.blockHash,
|
|
150
|
+
blockNumber: log.blockNumber.toString(), removed: false })), post });
|
|
151
|
+
const accounting = protocol.reconcileCollectProtocol({ plan, independentIntent: input, baseline: submit.baseline, check: submit.check, evidence }, { now: () => Math.floor(lpUserOpNow(runtime) / 1000) });
|
|
152
|
+
if (accounting.status !== 'PROTOCOL_ACCOUNTED')
|
|
153
|
+
return retain(`LP_USEROP_PROTOCOL_${accounting.reason}`);
|
|
154
|
+
requireLpUserOp(accounting.outcome === (confirmed.status === 'success' ? 'collected' : 'reverted') &&
|
|
155
|
+
same(accounting.userOpHash, binding.userOpHash) && same(accounting.transactionHash, binding.transactionHash), 'LP_USEROP_ACCOUNTING_BINDING');
|
|
156
|
+
const [finalizedAnchor, receiptAnchor, baselineAnchor, latestAnchor, chainId] = await Promise.all([
|
|
157
|
+
runtime.client.getBlock({ blockNumber: finalized.number }), runtime.client.getBlock({ blockNumber: receipt.blockNumber }),
|
|
158
|
+
runtime.client.getBlock({ blockNumber: BigInt(submit.baseline.blockNumber) }), runtime.client.getBlock({ blockNumber: latest.number }),
|
|
159
|
+
runtime.client.getChainId(),
|
|
160
|
+
]);
|
|
161
|
+
requireLpUserOp(chainId === terms.chainId && finalizedAnchor.hash === finalized.hash && receiptAnchor.hash === receipt.blockHash &&
|
|
162
|
+
baselineAnchor.hash === submit.baseline.blockHash && latestAnchor.hash === latest.hash &&
|
|
163
|
+
Math.floor(lpUserOpNow(runtime) / 1000) - Number(latest.timestamp) <= policy.maxHeadAgeSeconds, 'LP_USEROP_REORG');
|
|
164
|
+
return freezeHybridValue(LP_USEROP_SETTLEMENT_SCHEMA.parse({
|
|
165
|
+
...binding, blockHash: receipt.blockHash, blockNumber: receipt.blockNumber.toString(), userOpNonce: confirmed.nonce,
|
|
166
|
+
owner: terms.account, chainId: terms.chainId, pool: input.pool, tokenId: input.tokenId, planCommitment: plan.planCommitment,
|
|
167
|
+
baselineCommitment: accounting.baselineCommitment, accountingCommitment: accounting.accountingCommitment,
|
|
168
|
+
collectedRaw: accounting.collectedRaw, liquidityDelta: '0', ownerBalancesAtBlock: accounting.ownerBalancesAtBlock,
|
|
169
|
+
nftTokenApprovalCleared: true, erc20AllowancesCleared: true, actualGasCostWei: confirmed.actualGasCostWei,
|
|
170
|
+
actualGasUsed: confirmed.actualGasUsed, paymaster: confirmed.paymaster, profitClassification: 'UNKNOWN', distributableProfitRaw: null,
|
|
171
|
+
releaseClaim: true, retrySafe: false, finality: { policyCommitment: protocolCommitment(policy), observedAtMs,
|
|
172
|
+
finalizedBlockNumber: finalized.number.toString(), finalizedBlockHash: finalized.hash, confirmations: confirmations.toString() },
|
|
173
|
+
status: confirmed.status === 'success' ? 'completed' : 'reverted', protocolActionVerified: confirmed.status === 'success',
|
|
174
|
+
}));
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
// The shared sponsor circuit needs the exact proven breach, including after Stop and expiry.
|
|
179
|
+
if (error instanceof SponsoredReceiptUnconfirmedError && error.code === 'SPONSOR_GAS_CAP_EXCEEDED')
|
|
180
|
+
throw error;
|
|
181
|
+
return retain(error instanceof LpUserOpError ? error.code : 'LP_USEROP_EVIDENCE_UNAVAILABLE', 'unknown');
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=lp-userop-settlement.js.map
|