@kasufinance/kasu-sdk 2.8.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/README.md +120 -9
  2. package/dist/bundle.cjs.js +2068 -478
  3. package/dist/bundle.esm.js +2030 -480
  4. package/dist/domain/batch-proof.d.ts +210 -0
  5. package/dist/domain/batch-proof.js +384 -0
  6. package/dist/domain/batch-proof.js.map +1 -0
  7. package/dist/domain/explorer-urls.d.ts +33 -0
  8. package/dist/domain/explorer-urls.js +38 -0
  9. package/dist/domain/explorer-urls.js.map +1 -0
  10. package/dist/domain/fixed-term.d.ts +74 -0
  11. package/dist/domain/fixed-term.js +86 -0
  12. package/dist/domain/fixed-term.js.map +1 -0
  13. package/dist/domain/index.d.ts +12 -2
  14. package/dist/domain/index.js +7 -2
  15. package/dist/domain/index.js.map +1 -1
  16. package/dist/domain/partners.d.ts +29 -0
  17. package/dist/domain/partners.js +20 -0
  18. package/dist/domain/partners.js.map +1 -1
  19. package/dist/domain/requests.d.ts +24 -0
  20. package/dist/domain/requests.js +33 -0
  21. package/dist/domain/requests.js.map +1 -1
  22. package/dist/domain/sponsored-calls.d.ts +74 -0
  23. package/dist/domain/sponsored-calls.js +156 -0
  24. package/dist/domain/sponsored-calls.js.map +1 -0
  25. package/dist/domain/yields.d.ts +121 -0
  26. package/dist/domain/yields.js +151 -0
  27. package/dist/domain/yields.js.map +1 -0
  28. package/dist/facade/calls.d.ts +59 -0
  29. package/dist/facade/calls.js +89 -0
  30. package/dist/facade/calls.js.map +1 -0
  31. package/dist/facade/chain-configs.js +5 -0
  32. package/dist/facade/chain-configs.js.map +1 -1
  33. package/dist/facade/flows.d.ts +24 -3
  34. package/dist/facade/flows.js +22 -2
  35. package/dist/facade/flows.js.map +1 -1
  36. package/dist/facade/index.d.ts +3 -1
  37. package/dist/facade/index.js +1 -0
  38. package/dist/facade/index.js.map +1 -1
  39. package/dist/facade/kasu.d.ts +8 -2
  40. package/dist/facade/kasu.js +17 -5
  41. package/dist/facade/kasu.js.map +1 -1
  42. package/dist/facade/strategies.d.ts +43 -2
  43. package/dist/facade/strategies.js +80 -4
  44. package/dist/facade/strategies.js.map +1 -1
  45. package/dist/facade/types.d.ts +18 -0
  46. package/dist/flows/batch-errors.d.ts +74 -0
  47. package/dist/flows/batch-errors.js +83 -0
  48. package/dist/flows/batch-errors.js.map +1 -0
  49. package/dist/flows/batch-submission.d.ts +223 -0
  50. package/dist/flows/batch-submission.js +321 -0
  51. package/dist/flows/batch-submission.js.map +1 -0
  52. package/dist/flows/deposit-flow.d.ts +13 -20
  53. package/dist/flows/deposit-flow.js +14 -25
  54. package/dist/flows/deposit-flow.js.map +1 -1
  55. package/dist/flows/index.d.ts +6 -2
  56. package/dist/flows/index.js +4 -2
  57. package/dist/flows/index.js.map +1 -1
  58. package/dist/flows/withdraw-flow.d.ts +95 -4
  59. package/dist/flows/withdraw-flow.js +107 -2
  60. package/dist/flows/withdraw-flow.js.map +1 -1
  61. package/dist/services/DataService/queries.js +1 -0
  62. package/dist/services/DataService/queries.js.map +1 -1
  63. package/dist/services/UserLending/user-lending.d.ts +13 -0
  64. package/dist/services/UserLending/user-lending.js +25 -4
  65. package/dist/services/UserLending/user-lending.js.map +1 -1
  66. package/package.json +1 -1
  67. package/src/domain/batch-proof.ts +576 -0
  68. package/src/domain/explorer-urls.ts +74 -0
  69. package/src/domain/fixed-term.ts +115 -0
  70. package/src/domain/index.ts +56 -0
  71. package/src/domain/partners.ts +35 -0
  72. package/src/domain/requests.ts +37 -0
  73. package/src/domain/sponsored-calls.ts +213 -0
  74. package/src/domain/yields.ts +189 -0
  75. package/src/facade/calls.ts +135 -0
  76. package/src/facade/chain-configs.ts +5 -0
  77. package/src/facade/flows.ts +23 -2
  78. package/src/facade/index.ts +3 -0
  79. package/src/facade/kasu.ts +31 -2
  80. package/src/facade/strategies.ts +98 -7
  81. package/src/facade/types.ts +19 -0
  82. package/src/flows/batch-errors.ts +118 -0
  83. package/src/flows/batch-submission.ts +511 -0
  84. package/src/flows/deposit-flow.ts +27 -33
  85. package/src/flows/index.ts +33 -2
  86. package/src/flows/withdraw-flow.ts +219 -10
  87. package/src/services/DataService/queries.ts +1 -0
  88. package/src/services/UserLending/user-lending.ts +38 -10
@@ -0,0 +1,210 @@
1
+ import { BigNumber, BigNumberish } from 'ethers';
2
+ /**
3
+ * The money-path checks around an atomic, sponsored deposit batch — the
4
+ * `approve + requestDepositWithKyc` pair `DepositFlow` hands its
5
+ * `depositBatch` port.
6
+ *
7
+ * Two questions, both PURE:
8
+ *
9
+ * 1. Before anything is signed: is this batch exactly the deposit this run
10
+ * asked for? (`validateDepositBatchCalls`)
11
+ * 2. After it is mined: did THIS receipt deposit THIS amount, for THIS
12
+ * lender, into THIS tranche? (`proveDepositInReceipt`)
13
+ *
14
+ * Neither knows a relay, a wallet vendor or a URL; the app's submitter calls
15
+ * them.
16
+ */
17
+ /**
18
+ * ERC-4337 EntryPoint v0.7 — the contract a bundler calls `handleOps` on. The
19
+ * proof reads the user operation's outcome from ITS events, and from no other
20
+ * contract's.
21
+ */
22
+ export declare const ENTRY_POINT_V07 = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
23
+ /** One call of a batch, as a submitter receives it. */
24
+ export interface DepositBatchCall {
25
+ to: string;
26
+ data: string;
27
+ /** Native value. Absent or zero — neither call may send the native token. */
28
+ value?: BigNumberish;
29
+ }
30
+ /** What the run asked for. Every field is pinned on the batch. */
31
+ export interface DepositBatchExpectation {
32
+ /** The ERC-20 the deposit is paid in — the approval's target. */
33
+ stableAsset: string;
34
+ /** The contract approved AND called: the chain's `LendingPoolManager`. */
35
+ spender: string;
36
+ poolId: string;
37
+ trancheId: string;
38
+ /** The run's amount, in BASE units — approved and deposited, exactly. */
39
+ amount: BigNumberish;
40
+ /**
41
+ * The run's fixed-term config (`'0'` = variable). REQUIRED: a batch that
42
+ * locks the deposit into another term — or unlocks a fixed-term run into
43
+ * variable — is a different deposit, and `DepositFlow` always carries the
44
+ * value, so there is no consumer that genuinely lacks it.
45
+ */
46
+ fixedTermConfigId: BigNumberish;
47
+ }
48
+ export interface ValidatedDepositBatch {
49
+ /** The tranche the deposit goes into — what the receipt proof matches. */
50
+ tranche: string;
51
+ /** The exact amount approved and deposited, in base units. */
52
+ amount: BigNumber;
53
+ }
54
+ /**
55
+ * What `validateDepositBatchCalls` throws. `code` says which pin failed; the
56
+ * message names no party, so a wallet-rejection classifier can never read it
57
+ * as something the lender did.
58
+ */
59
+ export declare class DepositBatchMismatchError extends Error {
60
+ readonly code: string;
61
+ constructor(code: string);
62
+ }
63
+ /**
64
+ * Pin a deposit batch to the run that produced it — PURE; throws a
65
+ * `DepositBatchMismatchError` on ANY deviation, before the lender's key signs
66
+ * anything.
67
+ *
68
+ * The batch must be exactly two calls, in this order, with no native value:
69
+ *
70
+ * 0. `stableAsset.approve(spender, A)`;
71
+ * 1. `spender.requestDepositWithKyc(poolId, trancheId, A, '0x', fixedTermConfigId, …)`;
72
+ *
73
+ * with `A` the run's own amount in BOTH — never more, never unlimited (house
74
+ * rule: exact-amount approvals) — the run's own fixed-term config, each call
75
+ * the canonical ABI encoding of its arguments, and no swap data. The deposit
76
+ * data and KYC signature are not pinned here: they are signatures the
77
+ * contract and the backend verify.
78
+ *
79
+ * This is the client's half; a relaying backend checks the same shape again.
80
+ */
81
+ export declare function validateDepositBatchCalls(calls: readonly DepositBatchCall[], expected: DepositBatchExpectation): ValidatedDepositBatch;
82
+ /** A mined log, as an ethers (or any) receipt carries it. */
83
+ export interface ReceiptLog {
84
+ address: string;
85
+ topics: readonly string[];
86
+ data: string;
87
+ }
88
+ /** The two receipt fields the proof reads. An ethers v5 receipt fits. */
89
+ export interface ReceiptLike {
90
+ logs: readonly ReceiptLog[];
91
+ blockNumber: number;
92
+ }
93
+ export interface DepositProofExpectation {
94
+ /** The lender — the user operation's `sender`, and the deposit's `user`. */
95
+ lender: string;
96
+ tranche: string;
97
+ /** The exact amount deposited, in base units. */
98
+ amount: BigNumberish;
99
+ /**
100
+ * The user operation hash, when the submitter knows it. With it, only
101
+ * THAT operation counts; without it, the lender's ONLY operation in the
102
+ * bundle does — two of them without a hash is `ambiguous-operation`.
103
+ */
104
+ userOperationHash?: string | null;
105
+ /** Defaults to `ENTRY_POINT_V07`. */
106
+ entryPoint?: string;
107
+ /**
108
+ * The strategy's pending pool. When supplied, a `DepositRequested` counts
109
+ * only if THAT contract emitted it.
110
+ */
111
+ pendingPool?: string;
112
+ }
113
+ export type DepositProof =
114
+ /** This receipt shows this lender's deposit of this amount, in this tranche. */
115
+ {
116
+ kind: 'deposited';
117
+ dNftId: BigNumber;
118
+ blockNumber: number;
119
+ }
120
+ /**
121
+ * The user operation ran and reverted — the account rolled both calls
122
+ * back, nothing was deposited. `revertError` is the protocol error it
123
+ * named, or `''`.
124
+ */
125
+ | {
126
+ kind: 'reverted';
127
+ revertError: string;
128
+ }
129
+ /** The receipt does not show this deposit. NEVER a success, never a failure. */
130
+ | {
131
+ kind: 'unproven';
132
+ reason: 'no-user-operation' | 'ambiguous-operation' | 'no-deposit-requested' | 'malformed-receipt';
133
+ };
134
+ /**
135
+ * Did THIS receipt deposit for THIS lender? — PURE.
136
+ *
137
+ * A sponsored batch lands as a bundler's `handleOps` transaction, and that
138
+ * transaction's `status` is success even when the user operation inside it
139
+ * reverted: the EntryPoint catches the revert, the account rolls back both
140
+ * calls, nothing is deposited. So `status` decides NOTHING. What does:
141
+ *
142
+ * 1. The EntryPoint's `UserOperationEvent` for OUR operation — emitted BY
143
+ * the EntryPoint, `sender` = lender, and `userOpHash` = the known hash
144
+ * when there is one.
145
+ * 2. Its `success` flag. `false` → `reverted`, naming the protocol error
146
+ * from the matching `UserOperationRevertReason` when it decodes.
147
+ * 3. A `DepositRequested` with `user` = lender, `tranche` = the run's and
148
+ * `amount` = the run's, INSIDE our operation's execution: the logs after
149
+ * the previous operation's event (or `BeforeExecution`, which precedes
150
+ * the first) and before ours. A deposit from another operation in the
151
+ * same bundle does not count, and neither does one emitted by a contract
152
+ * that is not the pending pool, when the pending pool is known.
153
+ *
154
+ * Anything short of all three is `unproven`, which a consumer must treat as
155
+ * PENDING — the money may have moved — and never as a failure.
156
+ */
157
+ export declare function proveDepositInReceipt(receipt: ReceiptLike, expected: DepositProofExpectation): DepositProof;
158
+ export interface UserOperationProofExpectation {
159
+ /** The account that sent the operation — the lender's own wallet. */
160
+ sender: string;
161
+ /**
162
+ * The user operation hash, when the submitter knows it. With it, only
163
+ * THAT operation counts; without it, the sender's ONLY operation in the
164
+ * bundle does — two of them without a hash is `ambiguous-operation`.
165
+ */
166
+ userOperationHash?: string | null;
167
+ /** Defaults to `ENTRY_POINT_V07`. */
168
+ entryPoint?: string;
169
+ }
170
+ export type UserOperationProof =
171
+ /** The sender's operation ran and SUCCEEDED: every call in it executed. */
172
+ {
173
+ kind: 'executed';
174
+ blockNumber: number;
175
+ }
176
+ /**
177
+ * The operation ran and reverted — the account rolled its calls back.
178
+ * `revertError` is the protocol error it named, or `''`.
179
+ */
180
+ | {
181
+ kind: 'reverted';
182
+ revertError: string;
183
+ }
184
+ /**
185
+ * The receipt does not show the operation — or, with no hash to go by,
186
+ * shows TWO of the sender's and cannot tell which is ours. NEVER a
187
+ * success, never a failure.
188
+ */
189
+ | {
190
+ kind: 'unproven';
191
+ reason: 'no-user-operation' | 'ambiguous-operation' | 'malformed-receipt';
192
+ };
193
+ /**
194
+ * Did THIS receipt execute the sender's user operation? — PURE. The
195
+ * single-call sibling of `proveDepositInReceipt`, for a sponsored
196
+ * `requestWithdrawal`, a cancel or a fixed-term withdrawal: the same
197
+ * EntryPoint `UserOperationEvent` from the same contract, for the same
198
+ * sender (and hash, when known), read the same way — the `handleOps`
199
+ * transaction's own `status` decides NOTHING, because it is a success even
200
+ * when the operation inside it reverted.
201
+ *
202
+ * `executed` needs the operation's `success` flag. A single-call operation
203
+ * that succeeded executed its one call; there is no event to match beyond
204
+ * that, so pass the `userOperationHash` whenever the submitter reports one —
205
+ * without it, the sender's ONLY operation in the bundle is the one read, and a
206
+ * bundle holding two of theirs is `ambiguous-operation` (pending), never the
207
+ * first one's verdict: with one succeeded and one reverted, "executed" would
208
+ * tell the lender their request is queued when it is not.
209
+ */
210
+ export declare function proveUserOperationInReceipt(receipt: ReceiptLike, expected: UserOperationProofExpectation): UserOperationProof;
@@ -0,0 +1,384 @@
1
+ import { BigNumber, utils } from 'ethers';
2
+ import { IERC20MetadataAbi__factory } from '../contracts/factories/IERC20MetadataAbi__factory';
3
+ import { ILendingPoolManagerAbi__factory } from '../contracts/factories/ILendingPoolManagerAbi__factory';
4
+ import { decodeRevert } from './revert-errors';
5
+ /**
6
+ * The money-path checks around an atomic, sponsored deposit batch — the
7
+ * `approve + requestDepositWithKyc` pair `DepositFlow` hands its
8
+ * `depositBatch` port.
9
+ *
10
+ * Two questions, both PURE:
11
+ *
12
+ * 1. Before anything is signed: is this batch exactly the deposit this run
13
+ * asked for? (`validateDepositBatchCalls`)
14
+ * 2. After it is mined: did THIS receipt deposit THIS amount, for THIS
15
+ * lender, into THIS tranche? (`proveDepositInReceipt`)
16
+ *
17
+ * Neither knows a relay, a wallet vendor or a URL; the app's submitter calls
18
+ * them.
19
+ */
20
+ /**
21
+ * ERC-4337 EntryPoint v0.7 — the contract a bundler calls `handleOps` on. The
22
+ * proof reads the user operation's outcome from ITS events, and from no other
23
+ * contract's.
24
+ */
25
+ export const ENTRY_POINT_V07 = '0x0000000071727De22E5E9d8BAf0edAc6f37da032';
26
+ /** EntryPoint v0.7 events (eth-infinitism `IEntryPoint`). */
27
+ const ENTRY_POINT_EVENTS = new utils.Interface([
28
+ 'event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed)',
29
+ 'event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason)',
30
+ 'event BeforeExecution()',
31
+ ]);
32
+ /** kasu-contracts `IPendingPool` — emitted by the strategy's pending pool. */
33
+ const PENDING_POOL_EVENTS = new utils.Interface([
34
+ 'event DepositRequested(address indexed user, address indexed tranche, uint256 indexed dNftID, uint256 epochId, uint256 amount)',
35
+ ]);
36
+ const USER_OPERATION_EVENT = ENTRY_POINT_EVENTS.getEventTopic('UserOperationEvent');
37
+ const USER_OPERATION_REVERT_REASON = ENTRY_POINT_EVENTS.getEventTopic('UserOperationRevertReason');
38
+ const BEFORE_EXECUTION = ENTRY_POINT_EVENTS.getEventTopic('BeforeExecution');
39
+ const DEPOSIT_REQUESTED = PENDING_POOL_EVENTS.getEventTopic('DepositRequested');
40
+ const ERC20 = IERC20MetadataAbi__factory.createInterface();
41
+ const LENDING_POOL_MANAGER = ILendingPoolManagerAbi__factory.createInterface();
42
+ const ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
43
+ /**
44
+ * Case-insensitive address equality; `false` for anything that is not a
45
+ * 20-byte hex address. Checksums are not enforced — an all-lowercase address
46
+ * is the subgraph's own spelling.
47
+ */
48
+ function sameAddress(a, b) {
49
+ return (typeof a === 'string' &&
50
+ typeof b === 'string' &&
51
+ ADDRESS_RE.test(a) &&
52
+ ADDRESS_RE.test(b) &&
53
+ a.toLowerCase() === b.toLowerCase());
54
+ }
55
+ function isHexData(value) {
56
+ return typeof value === 'string' && utils.isHexString(value);
57
+ }
58
+ /**
59
+ * What `validateDepositBatchCalls` throws. `code` says which pin failed; the
60
+ * message names no party, so a wallet-rejection classifier can never read it
61
+ * as something the lender did.
62
+ */
63
+ export class DepositBatchMismatchError extends Error {
64
+ constructor(code) {
65
+ super(`Deposit batch does not match the run (${code})`);
66
+ this.name = 'DepositBatchMismatchError';
67
+ this.code = code;
68
+ Object.setPrototypeOf(this, DepositBatchMismatchError.prototype);
69
+ }
70
+ }
71
+ function mismatch(code) {
72
+ throw new DepositBatchMismatchError(code);
73
+ }
74
+ function isCallShape(call) {
75
+ if (call === null || typeof call !== 'object')
76
+ return false;
77
+ const { to, data } = call;
78
+ return typeof to === 'string' && ADDRESS_RE.test(to) && isHexData(data);
79
+ }
80
+ function isZeroValue(value) {
81
+ if (value === undefined)
82
+ return true;
83
+ try {
84
+ return BigNumber.from(value).isZero();
85
+ }
86
+ catch (_a) {
87
+ return false;
88
+ }
89
+ }
90
+ function sameAmount(a, b) {
91
+ return BigNumber.isBigNumber(a) && a.eq(b);
92
+ }
93
+ /**
94
+ * Decode `data` as a call to `name` on `iface`, and refuse it unless it is
95
+ * the CANONICAL encoding of what it decodes to. The ABI decoder tolerates
96
+ * trailing bytes and non-minimal offsets; a signature over bytes that decode
97
+ * "the same" but are not the bytes the SDK produced is not a signature over
98
+ * this run's deposit.
99
+ */
100
+ function decodeCanonical(iface, name, data, code) {
101
+ let parsed;
102
+ try {
103
+ parsed = iface.parseTransaction({ data });
104
+ }
105
+ catch (_a) {
106
+ return mismatch(code);
107
+ }
108
+ if (parsed.name !== name)
109
+ return mismatch(code);
110
+ let canonical;
111
+ try {
112
+ canonical = iface.encodeFunctionData(parsed.functionFragment, parsed.args);
113
+ }
114
+ catch (_b) {
115
+ return mismatch(code);
116
+ }
117
+ if (canonical.toLowerCase() !== data.toLowerCase()) {
118
+ return mismatch('non-canonical-encoding');
119
+ }
120
+ return parsed.args;
121
+ }
122
+ /**
123
+ * Pin a deposit batch to the run that produced it — PURE; throws a
124
+ * `DepositBatchMismatchError` on ANY deviation, before the lender's key signs
125
+ * anything.
126
+ *
127
+ * The batch must be exactly two calls, in this order, with no native value:
128
+ *
129
+ * 0. `stableAsset.approve(spender, A)`;
130
+ * 1. `spender.requestDepositWithKyc(poolId, trancheId, A, '0x', fixedTermConfigId, …)`;
131
+ *
132
+ * with `A` the run's own amount in BOTH — never more, never unlimited (house
133
+ * rule: exact-amount approvals) — the run's own fixed-term config, each call
134
+ * the canonical ABI encoding of its arguments, and no swap data. The deposit
135
+ * data and KYC signature are not pinned here: they are signatures the
136
+ * contract and the backend verify.
137
+ *
138
+ * This is the client's half; a relaying backend checks the same shape again.
139
+ */
140
+ export function validateDepositBatchCalls(calls, expected) {
141
+ const amount = BigNumber.from(expected.amount);
142
+ if (amount.lte(0))
143
+ mismatch('non-positive-amount');
144
+ if (!Array.isArray(calls) || calls.length !== 2)
145
+ mismatch('call-count');
146
+ const [approveCall, depositCall] = calls;
147
+ for (const call of [approveCall, depositCall]) {
148
+ if (!isCallShape(call))
149
+ mismatch('malformed-call');
150
+ if (!isZeroValue(call.value))
151
+ mismatch('native-value');
152
+ }
153
+ // 0 — the approval.
154
+ if (!sameAddress(approveCall.to, expected.stableAsset)) {
155
+ mismatch('approve-token');
156
+ }
157
+ const [approveSpender, approveAmount] = decodeCanonical(ERC20, 'approve', approveCall.data, 'approve-call');
158
+ if (!sameAddress(approveSpender, expected.spender)) {
159
+ mismatch('approve-spender');
160
+ }
161
+ // 1 — the deposit.
162
+ if (!sameAddress(depositCall.to, expected.spender)) {
163
+ mismatch('deposit-target');
164
+ }
165
+ const [pool, tranche, maxAmount, swapData, fixedTermConfigId] = decodeCanonical(LENDING_POOL_MANAGER, 'requestDepositWithKyc', depositCall.data, 'deposit-call');
166
+ if (!sameAddress(pool, expected.poolId))
167
+ mismatch('deposit-pool');
168
+ if (!sameAddress(tranche, expected.trancheId))
169
+ mismatch('deposit-tranche');
170
+ if (swapData !== '0x')
171
+ mismatch('swap-data');
172
+ if (!sameAmount(fixedTermConfigId, BigNumber.from(expected.fixedTermConfigId))) {
173
+ mismatch('fixed-term-config');
174
+ }
175
+ // EXACT amount, on both calls, equal to the run's.
176
+ if (!sameAmount(approveAmount, amount))
177
+ mismatch('approve-amount');
178
+ if (!sameAmount(maxAmount, amount))
179
+ mismatch('deposit-amount');
180
+ return { tranche: tranche, amount };
181
+ }
182
+ /** A log's first topic, lowercased; `undefined` for a log without a topic array. */
183
+ function topicOf(log) {
184
+ var _a;
185
+ return Array.isArray(log.topics)
186
+ ? (_a = log.topics[0]) === null || _a === void 0 ? void 0 : _a.toLowerCase()
187
+ : undefined;
188
+ }
189
+ function parse(iface, log) {
190
+ try {
191
+ return iface.parseLog({ topics: [...log.topics], data: log.data });
192
+ }
193
+ catch (_a) {
194
+ return null;
195
+ }
196
+ }
197
+ /**
198
+ * Did THIS receipt deposit for THIS lender? — PURE.
199
+ *
200
+ * A sponsored batch lands as a bundler's `handleOps` transaction, and that
201
+ * transaction's `status` is success even when the user operation inside it
202
+ * reverted: the EntryPoint catches the revert, the account rolls back both
203
+ * calls, nothing is deposited. So `status` decides NOTHING. What does:
204
+ *
205
+ * 1. The EntryPoint's `UserOperationEvent` for OUR operation — emitted BY
206
+ * the EntryPoint, `sender` = lender, and `userOpHash` = the known hash
207
+ * when there is one.
208
+ * 2. Its `success` flag. `false` → `reverted`, naming the protocol error
209
+ * from the matching `UserOperationRevertReason` when it decodes.
210
+ * 3. A `DepositRequested` with `user` = lender, `tranche` = the run's and
211
+ * `amount` = the run's, INSIDE our operation's execution: the logs after
212
+ * the previous operation's event (or `BeforeExecution`, which precedes
213
+ * the first) and before ours. A deposit from another operation in the
214
+ * same bundle does not count, and neither does one emitted by a contract
215
+ * that is not the pending pool, when the pending pool is known.
216
+ *
217
+ * Anything short of all three is `unproven`, which a consumer must treat as
218
+ * PENDING — the money may have moved — and never as a failure.
219
+ */
220
+ export function proveDepositInReceipt(receipt, expected) {
221
+ // Typed as an array, but a receipt is foreign data: check before reading.
222
+ if (!Array.isArray(receipt.logs)) {
223
+ return { kind: 'unproven', reason: 'malformed-receipt' };
224
+ }
225
+ const amount = BigNumber.from(expected.amount);
226
+ const op = locateUserOperation(receipt.logs, {
227
+ sender: expected.lender,
228
+ userOperationHash: expected.userOperationHash,
229
+ entryPoint: expected.entryPoint,
230
+ });
231
+ if (op === null)
232
+ return { kind: 'unproven', reason: 'no-user-operation' };
233
+ if (op === 'ambiguous') {
234
+ return { kind: 'unproven', reason: 'ambiguous-operation' };
235
+ }
236
+ if (!op.success) {
237
+ return { kind: 'reverted', revertError: op.revertError() };
238
+ }
239
+ for (const log of op.window) {
240
+ if (topicOf(log) !== DEPOSIT_REQUESTED)
241
+ continue;
242
+ if (expected.pendingPool !== undefined &&
243
+ !sameAddress(log.address, expected.pendingPool)) {
244
+ continue;
245
+ }
246
+ const decoded = parse(PENDING_POOL_EVENTS, log);
247
+ if ((decoded === null || decoded === void 0 ? void 0 : decoded.name) !== 'DepositRequested')
248
+ continue;
249
+ if (!sameAddress(decoded.args.user, expected.lender))
250
+ continue;
251
+ if (!sameAddress(decoded.args.tranche, expected.tranche))
252
+ continue;
253
+ if (!sameAmount(decoded.args.amount, amount))
254
+ continue;
255
+ return {
256
+ kind: 'deposited',
257
+ dNftId: decoded.args.dNftID,
258
+ blockNumber: receipt.blockNumber,
259
+ };
260
+ }
261
+ return { kind: 'unproven', reason: 'no-deposit-requested' };
262
+ }
263
+ /**
264
+ * Did THIS receipt execute the sender's user operation? — PURE. The
265
+ * single-call sibling of `proveDepositInReceipt`, for a sponsored
266
+ * `requestWithdrawal`, a cancel or a fixed-term withdrawal: the same
267
+ * EntryPoint `UserOperationEvent` from the same contract, for the same
268
+ * sender (and hash, when known), read the same way — the `handleOps`
269
+ * transaction's own `status` decides NOTHING, because it is a success even
270
+ * when the operation inside it reverted.
271
+ *
272
+ * `executed` needs the operation's `success` flag. A single-call operation
273
+ * that succeeded executed its one call; there is no event to match beyond
274
+ * that, so pass the `userOperationHash` whenever the submitter reports one —
275
+ * without it, the sender's ONLY operation in the bundle is the one read, and a
276
+ * bundle holding two of theirs is `ambiguous-operation` (pending), never the
277
+ * first one's verdict: with one succeeded and one reverted, "executed" would
278
+ * tell the lender their request is queued when it is not.
279
+ */
280
+ export function proveUserOperationInReceipt(receipt, expected) {
281
+ if (!Array.isArray(receipt.logs)) {
282
+ return { kind: 'unproven', reason: 'malformed-receipt' };
283
+ }
284
+ const op = locateUserOperation(receipt.logs, expected);
285
+ if (op === null)
286
+ return { kind: 'unproven', reason: 'no-user-operation' };
287
+ if (op === 'ambiguous') {
288
+ return { kind: 'unproven', reason: 'ambiguous-operation' };
289
+ }
290
+ if (!op.success) {
291
+ return { kind: 'reverted', revertError: op.revertError() };
292
+ }
293
+ return { kind: 'executed', blockNumber: receipt.blockNumber };
294
+ }
295
+ /**
296
+ * The sender's user operation in a `handleOps` receipt — its `success` flag
297
+ * and its execution window — `null` when the receipt does not show one, or
298
+ * `'ambiguous'` when no hash was given and it shows more than one of theirs.
299
+ *
300
+ * - The `UserOperationEvent` must be emitted BY the EntryPoint, with
301
+ * `sender` = the expected sender and, when one is known, `userOpHash` =
302
+ * the expected hash. Without a hash the sender must have exactly ONE
303
+ * operation in the bundle: a bundler may pack two of theirs (sequential
304
+ * nonces, or parallel nonce keys), and reading the first would attribute
305
+ * the other one's verdict to this call.
306
+ * - The window is the logs after the previous operation's event (or
307
+ * `BeforeExecution`, which precedes the first) and before ours: what our
308
+ * operation's calls emitted, and nothing another operation in the same
309
+ * bundle did.
310
+ */
311
+ function locateUserOperation(logs, expected) {
312
+ var _a, _b, _c;
313
+ const entryPoint = (_a = expected.entryPoint) !== null && _a !== void 0 ? _a : ENTRY_POINT_V07;
314
+ const wantedHash = (_c = (_b = expected.userOperationHash) === null || _b === void 0 ? void 0 : _b.toLowerCase()) !== null && _c !== void 0 ? _c : null;
315
+ const fromEntryPoint = (log) => sameAddress(log.address, entryPoint);
316
+ let opIndex = -1;
317
+ let success = false;
318
+ for (let i = 0; i < logs.length; i += 1) {
319
+ const log = logs[i];
320
+ if (!fromEntryPoint(log) || topicOf(log) !== USER_OPERATION_EVENT) {
321
+ continue;
322
+ }
323
+ const decoded = parse(ENTRY_POINT_EVENTS, log);
324
+ if ((decoded === null || decoded === void 0 ? void 0 : decoded.name) !== 'UserOperationEvent')
325
+ continue;
326
+ const userOpHash = String(decoded.args.userOpHash).toLowerCase();
327
+ if (wantedHash !== null && userOpHash !== wantedHash)
328
+ continue;
329
+ if (!sameAddress(decoded.args.sender, expected.sender))
330
+ continue;
331
+ // A second operation of this sender, and no hash to tell them apart.
332
+ if (opIndex >= 0)
333
+ return 'ambiguous';
334
+ opIndex = i;
335
+ success = decoded.args.success === true;
336
+ // A hash names exactly one operation; without one, keep scanning so
337
+ // a second operation of the sender is noticed.
338
+ if (wantedHash !== null)
339
+ break;
340
+ }
341
+ if (opIndex < 0)
342
+ return null;
343
+ // Our operation's execution window.
344
+ let windowStart = 0;
345
+ for (let i = opIndex - 1; i >= 0; i -= 1) {
346
+ const log = logs[i];
347
+ const topic = topicOf(log);
348
+ if (fromEntryPoint(log) &&
349
+ (topic === USER_OPERATION_EVENT || topic === BEFORE_EXECUTION)) {
350
+ windowStart = i + 1;
351
+ break;
352
+ }
353
+ }
354
+ const window = logs.slice(windowStart, opIndex);
355
+ return {
356
+ success,
357
+ window,
358
+ revertError: () => revertErrorIn(window, expected.sender, fromEntryPoint),
359
+ };
360
+ }
361
+ /**
362
+ * The protocol error a reverted operation named, or `''`. Only a PROTOCOL
363
+ * error is passed on: an `Error(string)` or an ERC-20 failure is not a name a
364
+ * consumer has words for.
365
+ */
366
+ function revertErrorIn(window, lender, fromEntryPoint) {
367
+ for (const log of window) {
368
+ if (!fromEntryPoint(log))
369
+ continue;
370
+ // Through the same guard as the main loop: a malformed log beside the
371
+ // revert reason must not turn a DEFINITIVE revert into "pending".
372
+ if (topicOf(log) !== USER_OPERATION_REVERT_REASON)
373
+ continue;
374
+ const decoded = parse(ENTRY_POINT_EVENTS, log);
375
+ if ((decoded === null || decoded === void 0 ? void 0 : decoded.name) !== 'UserOperationRevertReason')
376
+ continue;
377
+ if (!sameAddress(decoded.args.sender, lender))
378
+ continue;
379
+ const revert = decodeRevert(decoded.args.revertReason);
380
+ return (revert === null || revert === void 0 ? void 0 : revert.family) === 'protocol' ? revert.name : '';
381
+ }
382
+ return '';
383
+ }
384
+ //# sourceMappingURL=batch-proof.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-proof.js","sourceRoot":"","sources":["../../src/domain/batch-proof.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Block-explorer links, built only from a hash or address that has been
3
+ * checked first.
4
+ *
5
+ * Apps used to concatenate `explorer + '/tx/' + hash` from whatever string
6
+ * they held — a value off a receipt, a query parameter, a backend field. A
7
+ * string that is not a hash then becomes a link that points somewhere else
8
+ * (`/tx/../../evil`, `?x=`, a whole other URL). These helpers refuse anything
9
+ * that is not exactly a 32-byte hash or a 20-byte address, and refuse a base
10
+ * that is not an `https://` origin, so the only URL they can return is
11
+ * `<https base>/tx/<hash>` or `<https base>/address/<address>`.
12
+ *
13
+ * A URL is a locator, not copy — no words, no locale — so it does not
14
+ * stretch the domain layer's "numbers and codes only" rule.
15
+ *
16
+ * Pure: no network, no environment. They take any object with a
17
+ * `blockExplorerUrl` — a `CHAIN_CONFIGS` entry, or a custom one.
18
+ */
19
+ /** The one field these helpers read from a chain config. */
20
+ export interface ExplorerConfig {
21
+ blockExplorerUrl: string;
22
+ }
23
+ /**
24
+ * `<blockExplorerUrl>/tx/<txHash>`. Throws unless `txHash` is exactly `0x`
25
+ * followed by 64 hex digits.
26
+ */
27
+ export declare function explorerTxUrl(config: ExplorerConfig, txHash: string): string;
28
+ /**
29
+ * `<blockExplorerUrl>/address/<address>`, the address as given. Throws unless
30
+ * `address` is `0x` followed by 40 hex digits with a valid checksum when it
31
+ * is mixed-case (all-lower and all-upper carry no checksum and are accepted).
32
+ */
33
+ export declare function explorerAddressUrl(config: ExplorerConfig, address: string): string;