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,105 @@
|
|
|
1
|
+
/** Native yield contracts; separate from the existing LI.FI delegation envelope. */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { parseAbi } from 'viem';
|
|
4
|
+
import { DEFAULT_LIFI_FEE_BPS, DEFAULT_OTTO_LIFI_FEE_RECIPIENT } from './execution-config.js';
|
|
5
|
+
export const NATIVE_YIELD_FEE_BPS = DEFAULT_LIFI_FEE_BPS;
|
|
6
|
+
export const NATIVE_YIELD_FEE_RECIPIENT = DEFAULT_OTTO_LIFI_FEE_RECIPIENT;
|
|
7
|
+
export const NATIVE_YIELD_FEE_VERSION = 'native-yield-10bps-floor-v1';
|
|
8
|
+
export const NATIVE_YIELD_TTL_SECONDS = 180;
|
|
9
|
+
export const NATIVE_YIELD_STATE_MAX_AGE_MS = 60_000;
|
|
10
|
+
export const NATIVE_YIELD_ROUTER_ABI = parseAbi([
|
|
11
|
+
'function execute(bytes32 marketId, bool isDeposit, uint256 amount, uint256 minOutput, uint256 nonce, uint256 deadline) returns (uint256 outputAmount)',
|
|
12
|
+
'function nonces(address owner) view returns (uint256)',
|
|
13
|
+
'function marketConfig(bytes32 marketId) view returns (uint8 protocol, address asset, address position, address target, bool depositEnabled)',
|
|
14
|
+
'event YieldExecuted(address indexed owner, bytes32 indexed marketId, uint256 indexed nonce, bool isDeposit, uint256 inputAmount, uint256 outputAmount, uint256 feeAmount)',
|
|
15
|
+
]);
|
|
16
|
+
export const NATIVE_YIELD_ERC20_ABI = parseAbi([
|
|
17
|
+
'function approve(address spender, uint256 amount) returns (bool)',
|
|
18
|
+
'function allowance(address owner, address spender) view returns (uint256)',
|
|
19
|
+
'function balanceOf(address owner) view returns (uint256)',
|
|
20
|
+
'function decimals() view returns (uint8)',
|
|
21
|
+
'function totalSupply() view returns (uint256)',
|
|
22
|
+
'event Transfer(address indexed from, address indexed to, uint256 value)',
|
|
23
|
+
]);
|
|
24
|
+
const address = z.string().regex(/^0x[0-9a-fA-F]{40}$/);
|
|
25
|
+
const uint = z.string().regex(/^(0|[1-9]\d*)$/)
|
|
26
|
+
.refine((v) => BigInt(v) < 1n << 256n, 'must fit uint256');
|
|
27
|
+
const positive = uint.refine((v) => BigInt(v) > 0n, 'must be positive');
|
|
28
|
+
const hash = z.string().regex(/^0x[0-9a-fA-F]{64}$/);
|
|
29
|
+
const commitment = z.string().regex(/^sha256:[0-9a-f]{64}$/);
|
|
30
|
+
export const PREPARE_NATIVE_YIELD_INPUT_SCHEMA = z.object({
|
|
31
|
+
accountProfile: z.object({
|
|
32
|
+
kind: z.literal('eoa'), address, chainId: z.union([z.literal(8453), z.literal(42161)]),
|
|
33
|
+
}).strict(),
|
|
34
|
+
marketId: z.string().min(1).max(120),
|
|
35
|
+
registryCommitment: commitment,
|
|
36
|
+
action: z.enum(['deposit', 'withdraw']),
|
|
37
|
+
/** Deposit underlying units; withdraw exact nominal receipt-token units. */
|
|
38
|
+
amount: positive,
|
|
39
|
+
slippage: z.object({ maxBps: z.number().int().min(1).max(100), minOutput: positive.optional() }).strict(),
|
|
40
|
+
clientRef: z.string().trim().min(1).max(128).optional(),
|
|
41
|
+
}).strict();
|
|
42
|
+
export const NATIVE_YIELD_DEPLOYMENT_SCHEMA = z.object({
|
|
43
|
+
chainId: z.union([z.literal(8453), z.literal(42161)]),
|
|
44
|
+
address,
|
|
45
|
+
runtimeCodeHash: hash,
|
|
46
|
+
version: z.literal('native-yield-router-v1'),
|
|
47
|
+
reviewId: z.string().min(1).max(160),
|
|
48
|
+
}).strict();
|
|
49
|
+
/**
|
|
50
|
+
* Deliberately empty. No address/environment variable can activate a new spend
|
|
51
|
+
* target. Fill only in a separately approved exact-deployment review.
|
|
52
|
+
*/
|
|
53
|
+
export const NATIVE_YIELD_DEPLOYMENTS = Object.freeze([]);
|
|
54
|
+
const step = z.object({
|
|
55
|
+
kind: z.enum(['approve_reset', 'approve', 'call']),
|
|
56
|
+
to: address, value: z.literal('0'), data: z.string().regex(/^0x(?:[0-9a-fA-F]{2}){4,}$/),
|
|
57
|
+
}).strict();
|
|
58
|
+
export const NATIVE_YIELD_PLAN_SCHEMA = z.object({
|
|
59
|
+
schemaVersion: z.literal('1'),
|
|
60
|
+
kind: z.literal('native-yield-plan'),
|
|
61
|
+
transport: z.literal('native-protocol'),
|
|
62
|
+
marketId: z.string(),
|
|
63
|
+
registryCommitment: commitment,
|
|
64
|
+
owner: address,
|
|
65
|
+
chainId: z.union([z.literal(8453), z.literal(42161)]),
|
|
66
|
+
action: z.enum(['deposit', 'withdraw']),
|
|
67
|
+
amount: positive,
|
|
68
|
+
inputToken: address,
|
|
69
|
+
outputToken: address,
|
|
70
|
+
expectedOutput: positive,
|
|
71
|
+
minimumOutput: positive,
|
|
72
|
+
protocolOutput: positive,
|
|
73
|
+
fee: z.object({
|
|
74
|
+
bps: z.literal(10), recipient: address, token: address,
|
|
75
|
+
amount: uint, version: z.literal(NATIVE_YIELD_FEE_VERSION), rounding: z.literal('floor'),
|
|
76
|
+
}).strict(),
|
|
77
|
+
nonce: uint,
|
|
78
|
+
deadline: z.number().int().positive().safe(),
|
|
79
|
+
preparedAtMs: z.number().int().positive().safe(),
|
|
80
|
+
snapshot: z.object({ blockNumber: uint, blockHash: hash, observedAtMs: z.number().int().positive().safe() }).strict(),
|
|
81
|
+
deployment: NATIVE_YIELD_DEPLOYMENT_SCHEMA,
|
|
82
|
+
steps: z.array(step).length(3),
|
|
83
|
+
/** An unsigned plan does not authorize a wallet or confer a CDP delegation. */
|
|
84
|
+
authorization: z.literal('requires-separate-account-authority'),
|
|
85
|
+
allowanceRecovery: z.literal('approval-is-separate-and-may-remain-after-failure'),
|
|
86
|
+
intentCommitment: commitment,
|
|
87
|
+
planCommitment: commitment,
|
|
88
|
+
}).strict();
|
|
89
|
+
export function nativeYieldFee(gross) {
|
|
90
|
+
if (gross < 0n || gross >= 1n << 256n)
|
|
91
|
+
throw new Error('Invalid fee basis');
|
|
92
|
+
return gross / 1000n; // Fixed 10 bp, floor, overflow-free. Dust rounding is explicit.
|
|
93
|
+
}
|
|
94
|
+
export class NativeYieldRefusal extends Error {
|
|
95
|
+
code;
|
|
96
|
+
constructor(code, message) {
|
|
97
|
+
super(message);
|
|
98
|
+
this.code = code;
|
|
99
|
+
this.name = 'NativeYieldRefusal';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export function refuseNative(code, message) {
|
|
103
|
+
throw new NativeYieldRefusal(code, message);
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=native-yield-definition.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Fixed-block, owner-bound native preflight. Uses only the configured client's pinned market addresses. */
|
|
2
|
+
import { type PublicClient } from 'viem';
|
|
3
|
+
import { type NativeYieldReader } from './native-yield-definition.js';
|
|
4
|
+
export declare function viemNativeYieldReader(clients: Readonly<Partial<Record<number, PublicClient>>>, nowMs?: () => number): NativeYieldReader;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/** Fixed-block, owner-bound native preflight. Uses only the configured client's pinned market addresses. */
|
|
2
|
+
import { getAddress, keccak256, parseAbi, stringToHex } from 'viem';
|
|
3
|
+
import { NATIVE_YIELD_ERC20_ABI, NATIVE_YIELD_ROUTER_ABI, NATIVE_YIELD_STATE_MAX_AGE_MS, nativeYieldFee, refuseNative, } from './native-yield-definition.js';
|
|
4
|
+
const VAULT_ABI = parseAbi([
|
|
5
|
+
'function asset() view returns (address)',
|
|
6
|
+
'function maxDeposit(address receiver) view returns (uint256)',
|
|
7
|
+
'function maxRedeem(address owner) view returns (uint256)',
|
|
8
|
+
'function previewDeposit(uint256 assets) view returns (uint256)',
|
|
9
|
+
'function previewRedeem(uint256 shares) view returns (uint256)',
|
|
10
|
+
]);
|
|
11
|
+
const ATOKEN_ABI = parseAbi([
|
|
12
|
+
'function POOL() view returns (address)',
|
|
13
|
+
'function UNDERLYING_ASSET_ADDRESS() view returns (address)',
|
|
14
|
+
'function scaledTotalSupply() view returns (uint256)',
|
|
15
|
+
]);
|
|
16
|
+
const AAVE_ABI = parseAbi([
|
|
17
|
+
'function getConfiguration(address asset) view returns ((uint256 data))',
|
|
18
|
+
'function getVirtualUnderlyingBalance(address asset) view returns (uint128)',
|
|
19
|
+
'function getReserveNormalizedIncome(address asset) view returns (uint256)',
|
|
20
|
+
'function getReserveData(address asset) view returns (((uint256 data) configuration, uint128 liquidityIndex, uint128 currentLiquidityRate, uint128 variableBorrowIndex, uint128 currentVariableBorrowRate, uint128 currentStableBorrowRate, uint40 lastUpdateTimestamp, uint16 id, address aTokenAddress, address stableDebtTokenAddress, address variableDebtTokenAddress, address interestRateStrategyAddress, uint128 accruedToTreasury, uint128 unbacked, uint128 isolationModeTotalDebt))',
|
|
21
|
+
]);
|
|
22
|
+
const min = (a, b) => a < b ? a : b;
|
|
23
|
+
const RAY = 10n ** 27n;
|
|
24
|
+
export function viemNativeYieldReader(clients, nowMs = Date.now) {
|
|
25
|
+
return {
|
|
26
|
+
async read(market, owner, action, amount, deployment) {
|
|
27
|
+
const pub = clients[market.chainId];
|
|
28
|
+
if (!pub)
|
|
29
|
+
return refuseNative('NATIVE_CHAIN_UNAVAILABLE', 'No reviewed public reader for this chain');
|
|
30
|
+
if (await pub.getChainId() !== market.chainId) {
|
|
31
|
+
return refuseNative('NATIVE_CHAIN_MISMATCH', 'Configured RPC reports a different chain');
|
|
32
|
+
}
|
|
33
|
+
const block = await pub.getBlock({ blockTag: 'latest' });
|
|
34
|
+
if (block.number === null || !block.hash)
|
|
35
|
+
return refuseNative('NATIVE_STATE_UNAVAILABLE', 'Missing block anchor');
|
|
36
|
+
const observedAtMs = nowMs();
|
|
37
|
+
const blockTimestampMs = Number(block.timestamp) * 1000;
|
|
38
|
+
if (blockTimestampMs > observedAtMs + 5000 || observedAtMs - blockTimestampMs > NATIVE_YIELD_STATE_MAX_AGE_MS) {
|
|
39
|
+
return refuseNative('NATIVE_STATE_STALE', 'Protocol state is not current');
|
|
40
|
+
}
|
|
41
|
+
const blockNumber = block.number;
|
|
42
|
+
const read = async (address, abi, functionName, args = []) => await pub.readContract({ address, abi, functionName, args, blockNumber });
|
|
43
|
+
const router = getAddress(deployment.address);
|
|
44
|
+
const code = await pub.getCode({ address: router, blockNumber });
|
|
45
|
+
if (!code || keccak256(code).toLowerCase() !== deployment.runtimeCodeHash.toLowerCase()) {
|
|
46
|
+
return refuseNative('NATIVE_DEPLOYMENT_MISMATCH', 'Executor bytecode differs from the reviewed deployment');
|
|
47
|
+
}
|
|
48
|
+
const config = await read(router, NATIVE_YIELD_ROUTER_ABI, 'marketConfig', [keccak256(stringToHex(market.id))]);
|
|
49
|
+
if (config[0] !== (market.protocol === 'MORPHO' ? 1 : 2) ||
|
|
50
|
+
getAddress(config[1]) !== market.asset.address || getAddress(config[2]) !== market.position.tokenAddress ||
|
|
51
|
+
getAddress(config[3]) !== market.protocolTarget ||
|
|
52
|
+
config[4] !== (market.capabilities.native.deposit !== 'unavailable')) {
|
|
53
|
+
return refuseNative('NATIVE_MARKET_DRIFT', 'Executor market binding differs from the registry');
|
|
54
|
+
}
|
|
55
|
+
const inputToken = action === 'deposit' ? market.asset.address : market.position.tokenAddress;
|
|
56
|
+
const [assetDecimals, positionDecimals, assetBalance, positionBalance, inputAllowance, nativeBalance, routerNonce] = await Promise.all([
|
|
57
|
+
read(market.asset.address, NATIVE_YIELD_ERC20_ABI, 'decimals'),
|
|
58
|
+
read(market.position.tokenAddress, NATIVE_YIELD_ERC20_ABI, 'decimals'),
|
|
59
|
+
read(market.asset.address, NATIVE_YIELD_ERC20_ABI, 'balanceOf', [owner]),
|
|
60
|
+
read(market.position.tokenAddress, NATIVE_YIELD_ERC20_ABI, 'balanceOf', [owner]),
|
|
61
|
+
read(inputToken, NATIVE_YIELD_ERC20_ABI, 'allowance', [owner, router]),
|
|
62
|
+
pub.getBalance({ address: owner, blockNumber }),
|
|
63
|
+
read(router, NATIVE_YIELD_ROUTER_ABI, 'nonces', [owner]),
|
|
64
|
+
]);
|
|
65
|
+
if (assetDecimals !== market.asset.decimals || positionDecimals !== market.position.decimals) {
|
|
66
|
+
return refuseNative('NATIVE_TOKEN_DRIFT', 'Underlying or receipt-token decimals differ from the registry');
|
|
67
|
+
}
|
|
68
|
+
const inputBalance = action === 'deposit' ? assetBalance : positionBalance;
|
|
69
|
+
let maxInput;
|
|
70
|
+
let protocolOutput;
|
|
71
|
+
if (market.protocol === 'MORPHO') {
|
|
72
|
+
const asset = await read(market.protocolTarget, VAULT_ABI, 'asset');
|
|
73
|
+
if (getAddress(asset) !== market.asset.address)
|
|
74
|
+
return refuseNative('NATIVE_TOKEN_DRIFT', 'Vault underlying changed');
|
|
75
|
+
if (action === 'deposit') {
|
|
76
|
+
const net = amount - nativeYieldFee(amount);
|
|
77
|
+
const maxAssets = await read(market.protocolTarget, VAULT_ABI, 'maxDeposit', [owner]);
|
|
78
|
+
// Compare NET supply to capacity, but keep the owner's gross spend within balance.
|
|
79
|
+
maxInput = inputBalance;
|
|
80
|
+
if (net > maxAssets)
|
|
81
|
+
return refuseNative('NATIVE_CAPACITY', 'Requested net supply exceeds vault maxDeposit');
|
|
82
|
+
protocolOutput = await read(market.protocolTarget, VAULT_ABI, 'previewDeposit', [net]);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
maxInput = min(inputBalance, await read(market.protocolTarget, VAULT_ABI, 'maxRedeem', [owner]));
|
|
86
|
+
protocolOutput = await read(market.protocolTarget, VAULT_ABI, 'previewRedeem', [amount]);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
const [pool, underlying, reserve, normalizedIncome, scaledSupply] = await Promise.all([
|
|
91
|
+
read(market.position.tokenAddress, ATOKEN_ABI, 'POOL'),
|
|
92
|
+
read(market.position.tokenAddress, ATOKEN_ABI, 'UNDERLYING_ASSET_ADDRESS'),
|
|
93
|
+
read(market.protocolTarget, AAVE_ABI, 'getReserveData', [market.asset.address]),
|
|
94
|
+
read(market.protocolTarget, AAVE_ABI, 'getReserveNormalizedIncome', [market.asset.address]),
|
|
95
|
+
read(market.position.tokenAddress, ATOKEN_ABI, 'scaledTotalSupply'),
|
|
96
|
+
]);
|
|
97
|
+
if (getAddress(pool) !== market.protocolTarget || getAddress(underlying) !== market.asset.address ||
|
|
98
|
+
getAddress(reserve.aTokenAddress) !== market.position.tokenAddress) {
|
|
99
|
+
return refuseNative('NATIVE_TOKEN_DRIFT', 'Aave reserve, Pool and aToken binding disagree');
|
|
100
|
+
}
|
|
101
|
+
const flags = reserve.configuration.data;
|
|
102
|
+
const active = (flags & (1n << 56n)) !== 0n;
|
|
103
|
+
const frozen = (flags & (1n << 57n)) !== 0n;
|
|
104
|
+
const paused = (flags & (1n << 60n)) !== 0n;
|
|
105
|
+
if (!active || paused || (action === 'deposit' && frozen)) {
|
|
106
|
+
return refuseNative('NATIVE_RESERVE_UNAVAILABLE', 'Aave reserve is inactive, paused or frozen for supply');
|
|
107
|
+
}
|
|
108
|
+
if (action === 'deposit') {
|
|
109
|
+
const net = amount - nativeYieldFee(amount);
|
|
110
|
+
const cap = ((flags >> 116n) & ((1n << 36n) - 1n)) * 10n ** BigInt(market.asset.decimals);
|
|
111
|
+
// Round up conservatively and include treasury accrual, as Aave's cap validation does.
|
|
112
|
+
const accountedSupply = ((scaledSupply + reserve.accruedToTreasury) * normalizedIncome + RAY - 1n) / RAY;
|
|
113
|
+
if (cap !== 0n && accountedSupply + net > cap) {
|
|
114
|
+
return refuseNative('NATIVE_CAPACITY', 'Requested net supply exceeds Aave reserve supply capacity');
|
|
115
|
+
}
|
|
116
|
+
maxInput = inputBalance;
|
|
117
|
+
// aToken normalized balances can round by one atomic unit.
|
|
118
|
+
protocolOutput = net > 1n ? net - 1n : net;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
const liquidity = await read(market.protocolTarget, AAVE_ABI, 'getVirtualUnderlyingBalance', [market.asset.address]);
|
|
122
|
+
maxInput = min(inputBalance, liquidity);
|
|
123
|
+
protocolOutput = amount;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const anchor = await pub.getBlock({ blockNumber });
|
|
127
|
+
if (anchor.hash !== block.hash)
|
|
128
|
+
return refuseNative('NATIVE_STATE_REORG', 'Preflight block changed during reads');
|
|
129
|
+
return {
|
|
130
|
+
chainId: market.chainId, marketId: market.id, owner, blockNumber,
|
|
131
|
+
blockHash: block.hash, blockTimestampMs, observedAtMs, routerNonce, inputBalance,
|
|
132
|
+
inputAllowance, positionBalance, assetBalance, nativeBalance, maxInput, protocolOutput,
|
|
133
|
+
};
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=native-yield-reader.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** Receipt reconciliation only. This module never broadcasts, retries or changes an allowance. */
|
|
2
|
+
import { type Address, type Hex, type PublicClient, type TransactionReceipt } from 'viem';
|
|
3
|
+
import { type NativeYieldDeployment, type NativeYieldPlan } from './native-yield-definition.js';
|
|
4
|
+
export type NativeYieldSettlement = {
|
|
5
|
+
readonly status: 'unknown' | 'pending' | 'needs-attention';
|
|
6
|
+
readonly reason: string;
|
|
7
|
+
readonly transactionHash: Hex;
|
|
8
|
+
readonly retrySafe: false;
|
|
9
|
+
} | {
|
|
10
|
+
readonly status: 'reverted';
|
|
11
|
+
readonly transactionHash: Hex;
|
|
12
|
+
readonly remainingAllowance: string;
|
|
13
|
+
readonly retrySafe: false;
|
|
14
|
+
} | {
|
|
15
|
+
readonly status: 'completed';
|
|
16
|
+
readonly transactionHash: Hex;
|
|
17
|
+
readonly blockNumber: string;
|
|
18
|
+
readonly owner: Address;
|
|
19
|
+
readonly marketId: string;
|
|
20
|
+
readonly action: 'deposit' | 'withdraw';
|
|
21
|
+
readonly actualInput: string;
|
|
22
|
+
readonly actualOutput: string;
|
|
23
|
+
readonly actualOttoFee: string;
|
|
24
|
+
readonly feeToken: Address;
|
|
25
|
+
readonly inputAllowance: '0';
|
|
26
|
+
readonly protocolAllowance: '0';
|
|
27
|
+
readonly positionBalanceAtBlock: string;
|
|
28
|
+
readonly assetBalanceAtBlock: string;
|
|
29
|
+
readonly protocolActionVerified: true;
|
|
30
|
+
readonly gasUsed: string;
|
|
31
|
+
readonly receiptGasCostWei: string;
|
|
32
|
+
readonly excludesApprovalTransactionCosts: true;
|
|
33
|
+
readonly retrySafe: false;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Match isolated transaction events, never a quote estimate or a whole-block
|
|
37
|
+
* balance difference (which can include unrelated transfers and aToken interest).
|
|
38
|
+
*/
|
|
39
|
+
export declare function nativeYieldReceiptAmounts(plan: NativeYieldPlan, receipt: TransactionReceipt): {
|
|
40
|
+
owner: Address;
|
|
41
|
+
marketId: Hex;
|
|
42
|
+
nonce: bigint;
|
|
43
|
+
isDeposit: boolean;
|
|
44
|
+
inputAmount: bigint;
|
|
45
|
+
outputAmount: bigint;
|
|
46
|
+
feeAmount: bigint;
|
|
47
|
+
};
|
|
48
|
+
export declare function readNativeYieldSettlement(pub: PublicClient, rawPlan: NativeYieldPlan, transactionHash: Hex, deployments?: readonly NativeYieldDeployment[]): Promise<NativeYieldSettlement>;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/** Receipt reconciliation only. This module never broadcasts, retries or changes an allowance. */
|
|
2
|
+
import { decodeEventLog, getAddress, keccak256, parseAbi, stringToHex, } from 'viem';
|
|
3
|
+
import { freezeHybridValue, getHybridYieldMarket, hybridYieldCommitment } from './hybrid-yield-markets.js';
|
|
4
|
+
import { NATIVE_YIELD_DEPLOYMENTS, NATIVE_YIELD_ERC20_ABI, NATIVE_YIELD_FEE_RECIPIENT, NATIVE_YIELD_PLAN_SCHEMA, NATIVE_YIELD_ROUTER_ABI, nativeYieldFee, } from './native-yield-definition.js';
|
|
5
|
+
const PROTOCOL_EVENTS = parseAbi([
|
|
6
|
+
'event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares)',
|
|
7
|
+
'event Withdraw(address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares)',
|
|
8
|
+
'event Supply(address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, uint16 indexed referralCode)',
|
|
9
|
+
]);
|
|
10
|
+
const AAVE_WITHDRAW = parseAbi([
|
|
11
|
+
'event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount)',
|
|
12
|
+
]);
|
|
13
|
+
function events(receipt, address, abi) {
|
|
14
|
+
return receipt.logs.filter((l) => l.address.toLowerCase() === address.toLowerCase()).flatMap((log) => {
|
|
15
|
+
try {
|
|
16
|
+
return [decodeEventLog({ abi, data: log.data, topics: log.topics, strict: true })];
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function same(a, b) { return typeof a === 'string' && a.toLowerCase() === b.toLowerCase(); }
|
|
24
|
+
/**
|
|
25
|
+
* Match isolated transaction events, never a quote estimate or a whole-block
|
|
26
|
+
* balance difference (which can include unrelated transfers and aToken interest).
|
|
27
|
+
*/
|
|
28
|
+
export function nativeYieldReceiptAmounts(plan, receipt) {
|
|
29
|
+
const market = getHybridYieldMarket(plan.marketId);
|
|
30
|
+
if (!market || hybridYieldCommitment(market) !== plan.registryCommitment)
|
|
31
|
+
throw new Error('Market commitment mismatch');
|
|
32
|
+
const router = plan.deployment.address;
|
|
33
|
+
const matches = events(receipt, router, NATIVE_YIELD_ROUTER_ABI).filter((e) => e.eventName === 'YieldExecuted');
|
|
34
|
+
if (matches.length !== 1)
|
|
35
|
+
throw new Error('Expected exactly one native execution event');
|
|
36
|
+
const event = matches[0].args;
|
|
37
|
+
if (!same(event.owner, plan.owner) || event.marketId !== keccak256(stringToHex(market.id)) ||
|
|
38
|
+
event.nonce !== BigInt(plan.nonce) || event.isDeposit !== (plan.action === 'deposit') ||
|
|
39
|
+
event.inputAmount !== BigInt(plan.amount) || event.outputAmount < BigInt(plan.minimumOutput)) {
|
|
40
|
+
throw new Error('Native receipt differs from the reviewed owner/action/amount/minimum/nonce');
|
|
41
|
+
}
|
|
42
|
+
const grossAssets = event.isDeposit ? event.inputAmount : event.outputAmount + event.feeAmount;
|
|
43
|
+
if (event.feeAmount !== nativeYieldFee(grossAssets))
|
|
44
|
+
throw new Error('Native fee amount mismatch');
|
|
45
|
+
const transfers = events(receipt, market.asset.address, NATIVE_YIELD_ERC20_ABI)
|
|
46
|
+
.filter((e) => e.eventName === 'Transfer')
|
|
47
|
+
.map((e) => e.args);
|
|
48
|
+
const sentTo = (recipient) => transfers
|
|
49
|
+
.filter((e) => same(e.from, router) && same(e.to, recipient)).map((e) => e.value);
|
|
50
|
+
const equals = (actual, expected) => actual.length === expected.length && actual.every((v, i) => v === expected[i]);
|
|
51
|
+
const feeLegs = event.feeAmount > 0n ? [event.feeAmount] : [];
|
|
52
|
+
if (!event.isDeposit && same(plan.owner, NATIVE_YIELD_FEE_RECIPIENT)) {
|
|
53
|
+
// The two transfers have identical endpoints; bind their exact ordered values
|
|
54
|
+
// instead of incorrectly counting the gross sum as both fee and net proceeds.
|
|
55
|
+
if (!equals(sentTo(plan.owner), [...feeLegs, event.outputAmount])) {
|
|
56
|
+
throw new Error('Exact fee and net exit transfers to the owning fee account are missing');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
if (!equals(sentTo(NATIVE_YIELD_FEE_RECIPIENT), feeLegs)) {
|
|
61
|
+
throw new Error('Actual Otto fee transfer is missing or differs');
|
|
62
|
+
}
|
|
63
|
+
if (!event.isDeposit && !equals(sentTo(plan.owner), [event.outputAmount])) {
|
|
64
|
+
throw new Error('Exact net exit proceeds were not transferred to the owner');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (market.protocol === 'MORPHO') {
|
|
68
|
+
const protocol = events(receipt, market.protocolTarget, PROTOCOL_EVENTS).filter((e) => e.eventName === (event.isDeposit ? 'Deposit' : 'Withdraw'));
|
|
69
|
+
const matched = protocol.filter((e) => {
|
|
70
|
+
const a = e.args;
|
|
71
|
+
return same(a.sender, router) && same(a.owner, event.isDeposit ? plan.owner : router) &&
|
|
72
|
+
(event.isDeposit || same(a.receiver, router)) &&
|
|
73
|
+
a.assets === (event.isDeposit ? event.inputAmount - event.feeAmount : grossAssets) &&
|
|
74
|
+
a.shares === (event.isDeposit ? event.outputAmount : event.inputAmount);
|
|
75
|
+
});
|
|
76
|
+
if (matched.length !== 1)
|
|
77
|
+
throw new Error('Exact Morpho deposit/redeem event is missing');
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
const protocol = events(receipt, market.protocolTarget, event.isDeposit ? PROTOCOL_EVENTS : AAVE_WITHDRAW)
|
|
81
|
+
.filter((e) => e.eventName === (event.isDeposit ? 'Supply' : 'Withdraw'));
|
|
82
|
+
const matched = protocol.filter((e) => {
|
|
83
|
+
const a = e.args;
|
|
84
|
+
return same(a.reserve, market.asset.address) && same(a.user, router) &&
|
|
85
|
+
(event.isDeposit ? same(a.onBehalfOf, plan.owner) : same(a.to, router)) &&
|
|
86
|
+
a.amount === (event.isDeposit ? event.inputAmount - event.feeAmount : grossAssets);
|
|
87
|
+
});
|
|
88
|
+
if (matched.length !== 1)
|
|
89
|
+
throw new Error('Exact Aave supply/withdraw event is missing');
|
|
90
|
+
}
|
|
91
|
+
return event;
|
|
92
|
+
}
|
|
93
|
+
export async function readNativeYieldSettlement(pub, rawPlan, transactionHash, deployments = NATIVE_YIELD_DEPLOYMENTS) {
|
|
94
|
+
const pending = (status, reason) => ({ status, reason, transactionHash, retrySafe: false });
|
|
95
|
+
try {
|
|
96
|
+
const plan = NATIVE_YIELD_PLAN_SCHEMA.parse(rawPlan);
|
|
97
|
+
const { planCommitment, ...core } = plan;
|
|
98
|
+
if (hybridYieldCommitment(core) !== planCommitment)
|
|
99
|
+
return pending('needs-attention', 'Plan commitment mismatch');
|
|
100
|
+
const deployment = deployments.find((d) => d.chainId === plan.chainId);
|
|
101
|
+
if (!deployment || hybridYieldCommitment(deployment) !== hybridYieldCommitment(plan.deployment)) {
|
|
102
|
+
return pending('needs-attention', 'Executor deployment is not approved');
|
|
103
|
+
}
|
|
104
|
+
if (await pub.getChainId() !== plan.chainId)
|
|
105
|
+
return pending('needs-attention', 'Receipt reader is on another chain');
|
|
106
|
+
const [tx, receipt] = await Promise.all([
|
|
107
|
+
pub.getTransaction({ hash: transactionHash }), pub.getTransactionReceipt({ hash: transactionHash }),
|
|
108
|
+
]);
|
|
109
|
+
if (!same(tx.from, plan.owner) || !same(tx.to, deployment.address) || tx.value !== 0n ||
|
|
110
|
+
tx.input.toLowerCase() !== plan.steps[2].data.toLowerCase() ||
|
|
111
|
+
receipt.transactionHash.toLowerCase() !== transactionHash.toLowerCase()) {
|
|
112
|
+
return pending('needs-attention', 'Known transaction differs from the reviewed action');
|
|
113
|
+
}
|
|
114
|
+
const [canonical, finalized, code] = await Promise.all([
|
|
115
|
+
pub.getBlock({ blockNumber: receipt.blockNumber }), pub.getBlock({ blockTag: 'finalized' }),
|
|
116
|
+
pub.getCode({ address: getAddress(deployment.address), blockNumber: receipt.blockNumber }),
|
|
117
|
+
]);
|
|
118
|
+
if (canonical.hash !== receipt.blockHash || !code ||
|
|
119
|
+
keccak256(code).toLowerCase() !== deployment.runtimeCodeHash.toLowerCase()) {
|
|
120
|
+
return pending('needs-attention', 'Receipt block or executor bytecode is not canonical');
|
|
121
|
+
}
|
|
122
|
+
if (finalized.number === null || finalized.number < receipt.blockNumber) {
|
|
123
|
+
return pending('pending', 'Known receipt is awaiting canonical finality');
|
|
124
|
+
}
|
|
125
|
+
const allowance = await pub.readContract({
|
|
126
|
+
address: getAddress(plan.inputToken), abi: NATIVE_YIELD_ERC20_ABI, functionName: 'allowance',
|
|
127
|
+
args: [getAddress(plan.owner), getAddress(deployment.address)], blockNumber: receipt.blockNumber,
|
|
128
|
+
});
|
|
129
|
+
if (receipt.status === 'reverted')
|
|
130
|
+
return { status: 'reverted', transactionHash, remainingAllowance: allowance.toString(), retrySafe: false };
|
|
131
|
+
const market = getHybridYieldMarket(plan.marketId);
|
|
132
|
+
if (!market)
|
|
133
|
+
return pending('needs-attention', 'Unknown position');
|
|
134
|
+
const amounts = nativeYieldReceiptAmounts(plan, receipt);
|
|
135
|
+
const [protocolAllowance, positionBalance, assetBalance, nonce] = await Promise.all([
|
|
136
|
+
pub.readContract({ address: market.asset.address, abi: NATIVE_YIELD_ERC20_ABI, functionName: 'allowance',
|
|
137
|
+
args: [getAddress(deployment.address), market.protocolTarget], blockNumber: receipt.blockNumber }),
|
|
138
|
+
pub.readContract({ address: market.position.tokenAddress, abi: NATIVE_YIELD_ERC20_ABI, functionName: 'balanceOf',
|
|
139
|
+
args: [getAddress(plan.owner)], blockNumber: receipt.blockNumber }),
|
|
140
|
+
pub.readContract({ address: market.asset.address, abi: NATIVE_YIELD_ERC20_ABI, functionName: 'balanceOf',
|
|
141
|
+
args: [getAddress(plan.owner)], blockNumber: receipt.blockNumber }),
|
|
142
|
+
pub.readContract({ address: getAddress(deployment.address), abi: NATIVE_YIELD_ROUTER_ABI, functionName: 'nonces',
|
|
143
|
+
args: [getAddress(plan.owner)], blockNumber: receipt.blockNumber }),
|
|
144
|
+
]);
|
|
145
|
+
if (allowance !== 0n || protocolAllowance !== 0n || nonce < BigInt(plan.nonce) + 1n) {
|
|
146
|
+
return pending('needs-attention', 'Execution observed, but allowance or nonce recovery is unresolved');
|
|
147
|
+
}
|
|
148
|
+
const recheck = await pub.getBlock({ blockNumber: receipt.blockNumber });
|
|
149
|
+
if (recheck.hash !== receipt.blockHash)
|
|
150
|
+
return pending('pending', 'Receipt changed while reconciling');
|
|
151
|
+
return freezeHybridValue({
|
|
152
|
+
status: 'completed', transactionHash, blockNumber: receipt.blockNumber.toString(),
|
|
153
|
+
owner: getAddress(plan.owner), marketId: market.id, action: plan.action,
|
|
154
|
+
actualInput: amounts.inputAmount.toString(), actualOutput: amounts.outputAmount.toString(),
|
|
155
|
+
actualOttoFee: amounts.feeAmount.toString(), feeToken: market.asset.address,
|
|
156
|
+
inputAllowance: '0', protocolAllowance: '0', positionBalanceAtBlock: positionBalance.toString(),
|
|
157
|
+
assetBalanceAtBlock: assetBalance.toString(), protocolActionVerified: true,
|
|
158
|
+
gasUsed: receipt.gasUsed.toString(), receiptGasCostWei: (receipt.gasUsed * receipt.effectiveGasPrice).toString(),
|
|
159
|
+
excludesApprovalTransactionCosts: true,
|
|
160
|
+
retrySafe: false,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return pending('unknown', 'Transaction or position evidence is unavailable; reconcile this hash before any new spend');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=native-yield-settlement.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Simulation after exact approval, with no state overrides and no signer. */
|
|
2
|
+
import { type PublicClient } from 'viem';
|
|
3
|
+
import type { NativeYieldRuntime } from './native-yield.js';
|
|
4
|
+
import { type NativeYieldVerificationContext } from './native-yield-verify.js';
|
|
5
|
+
/**
|
|
6
|
+
* Run after approval is mined, immediately before the signing boundary. A passing
|
|
7
|
+
* simulation is a snapshot, not wallet authority or a guarantee of later execution.
|
|
8
|
+
* RPC gas estimates follow each chain's fee semantics; additional chain charges
|
|
9
|
+
* may apply. Already-mined approval transactions are separately accounted.
|
|
10
|
+
*/
|
|
11
|
+
export declare function simulateNativeYieldPlan(pub: PublicClient, runtime: NativeYieldRuntime, rawPlan: unknown, rawInput: unknown, context: NativeYieldVerificationContext): Promise<{
|
|
12
|
+
verified: import("./native-yield-verify.js").VerifiedNativeYieldPlan;
|
|
13
|
+
simulatedOutput: string;
|
|
14
|
+
estimatedGasLimit: string;
|
|
15
|
+
rpcGasEstimateWei: string;
|
|
16
|
+
excludesApprovalTransactionCosts: true;
|
|
17
|
+
simulationOnly: true;
|
|
18
|
+
}>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Simulation after exact approval, with no state overrides and no signer. */
|
|
2
|
+
import { decodeAbiParameters, getAddress, parseAbiParameters } from 'viem';
|
|
3
|
+
import { freezeHybridValue } from './hybrid-yield-markets.js';
|
|
4
|
+
import { NATIVE_YIELD_ERC20_ABI, refuseNative } from './native-yield-definition.js';
|
|
5
|
+
import { verifyNativeYieldPlan } from './native-yield-verify.js';
|
|
6
|
+
/**
|
|
7
|
+
* Run after approval is mined, immediately before the signing boundary. A passing
|
|
8
|
+
* simulation is a snapshot, not wallet authority or a guarantee of later execution.
|
|
9
|
+
* RPC gas estimates follow each chain's fee semantics; additional chain charges
|
|
10
|
+
* may apply. Already-mined approval transactions are separately accounted.
|
|
11
|
+
*/
|
|
12
|
+
export async function simulateNativeYieldPlan(pub, runtime, rawPlan, rawInput, context) {
|
|
13
|
+
const verified = await verifyNativeYieldPlan(runtime, rawPlan, rawInput, context);
|
|
14
|
+
const plan = verified.plan;
|
|
15
|
+
if (await pub.getChainId() !== plan.chainId)
|
|
16
|
+
refuseNative('NATIVE_CHAIN_MISMATCH', 'Simulation chain differs');
|
|
17
|
+
const owner = getAddress(plan.owner);
|
|
18
|
+
const router = getAddress(plan.deployment.address);
|
|
19
|
+
const allowance = await pub.readContract({
|
|
20
|
+
address: getAddress(plan.inputToken), abi: NATIVE_YIELD_ERC20_ABI, functionName: 'allowance',
|
|
21
|
+
args: [owner, router],
|
|
22
|
+
});
|
|
23
|
+
if (allowance !== BigInt(plan.amount)) {
|
|
24
|
+
refuseNative('NATIVE_APPROVAL_REQUIRED', 'Reconcile/reset and mine the exact input approval before simulation');
|
|
25
|
+
}
|
|
26
|
+
const request = { account: owner, to: router, data: plan.steps[2].data, value: 0n };
|
|
27
|
+
const [simulation, gas, gasPrice, nativeBalance] = await Promise.all([
|
|
28
|
+
pub.call(request), pub.estimateGas(request), pub.getGasPrice(), pub.getBalance({ address: owner }),
|
|
29
|
+
]);
|
|
30
|
+
if (!simulation.data)
|
|
31
|
+
refuseNative('NATIVE_SIMULATION_FAILED', 'Native call returned no output');
|
|
32
|
+
const [actualOutput] = decodeAbiParameters(parseAbiParameters('uint256'), simulation.data);
|
|
33
|
+
if (actualOutput < BigInt(plan.minimumOutput))
|
|
34
|
+
refuseNative('NATIVE_SIMULATION_FAILED', 'Simulation output is below the reviewed minimum');
|
|
35
|
+
const gasLimit = (gas * 120n + 99n) / 100n;
|
|
36
|
+
if (nativeBalance < gasLimit * gasPrice)
|
|
37
|
+
refuseNative('NATIVE_GAS_REQUIRED', 'Native balance cannot cover the estimated execution gas');
|
|
38
|
+
return freezeHybridValue({
|
|
39
|
+
verified, simulatedOutput: actualOutput.toString(), estimatedGasLimit: gasLimit.toString(),
|
|
40
|
+
rpcGasEstimateWei: (gasLimit * gasPrice).toString(),
|
|
41
|
+
excludesApprovalTransactionCosts: true,
|
|
42
|
+
simulationOnly: true,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=native-yield-simulation.js.map
|