@kasufinance/kasu-sdk 2.7.1 → 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.
- package/README.md +162 -7
- package/dist/bundle.cjs.js +2292 -449
- package/dist/bundle.esm.js +2253 -451
- package/dist/domain/batch-proof.d.ts +210 -0
- package/dist/domain/batch-proof.js +384 -0
- package/dist/domain/batch-proof.js.map +1 -0
- package/dist/domain/explorer-urls.d.ts +33 -0
- package/dist/domain/explorer-urls.js +38 -0
- package/dist/domain/explorer-urls.js.map +1 -0
- package/dist/domain/fixed-term.d.ts +74 -0
- package/dist/domain/fixed-term.js +86 -0
- package/dist/domain/fixed-term.js.map +1 -0
- package/dist/domain/index.d.ts +12 -2
- package/dist/domain/index.js +7 -2
- package/dist/domain/index.js.map +1 -1
- package/dist/domain/partners.d.ts +29 -0
- package/dist/domain/partners.js +20 -0
- package/dist/domain/partners.js.map +1 -1
- package/dist/domain/requests.d.ts +24 -0
- package/dist/domain/requests.js +33 -0
- package/dist/domain/requests.js.map +1 -1
- package/dist/domain/sponsored-calls.d.ts +74 -0
- package/dist/domain/sponsored-calls.js +156 -0
- package/dist/domain/sponsored-calls.js.map +1 -0
- package/dist/domain/yields.d.ts +121 -0
- package/dist/domain/yields.js +151 -0
- package/dist/domain/yields.js.map +1 -0
- package/dist/facade/calls.d.ts +59 -0
- package/dist/facade/calls.js +89 -0
- package/dist/facade/calls.js.map +1 -0
- package/dist/facade/chain-configs.js +5 -0
- package/dist/facade/chain-configs.js.map +1 -1
- package/dist/facade/flows.d.ts +40 -3
- package/dist/facade/flows.js +36 -3
- package/dist/facade/flows.js.map +1 -1
- package/dist/facade/index.d.ts +3 -1
- package/dist/facade/index.js +1 -0
- package/dist/facade/index.js.map +1 -1
- package/dist/facade/kasu.d.ts +8 -2
- package/dist/facade/kasu.js +17 -4
- package/dist/facade/kasu.js.map +1 -1
- package/dist/facade/strategies.d.ts +43 -2
- package/dist/facade/strategies.js +80 -4
- package/dist/facade/strategies.js.map +1 -1
- package/dist/facade/types.d.ts +18 -0
- package/dist/flows/batch-errors.d.ts +74 -0
- package/dist/flows/batch-errors.js +83 -0
- package/dist/flows/batch-errors.js.map +1 -0
- package/dist/flows/batch-submission.d.ts +223 -0
- package/dist/flows/batch-submission.js +321 -0
- package/dist/flows/batch-submission.js.map +1 -0
- package/dist/flows/deposit-flow.d.ts +128 -4
- package/dist/flows/deposit-flow.js +233 -4
- package/dist/flows/deposit-flow.js.map +1 -1
- package/dist/flows/index.d.ts +6 -2
- package/dist/flows/index.js +3 -1
- package/dist/flows/index.js.map +1 -1
- package/dist/flows/withdraw-flow.d.ts +95 -4
- package/dist/flows/withdraw-flow.js +107 -2
- package/dist/flows/withdraw-flow.js.map +1 -1
- package/dist/services/DataService/queries.js +1 -0
- package/dist/services/DataService/queries.js.map +1 -1
- package/dist/services/UserLending/user-lending.d.ts +13 -0
- package/dist/services/UserLending/user-lending.js +25 -4
- package/dist/services/UserLending/user-lending.js.map +1 -1
- package/package.json +1 -1
- package/src/domain/batch-proof.ts +576 -0
- package/src/domain/explorer-urls.ts +74 -0
- package/src/domain/fixed-term.ts +115 -0
- package/src/domain/index.ts +56 -0
- package/src/domain/partners.ts +35 -0
- package/src/domain/requests.ts +37 -0
- package/src/domain/sponsored-calls.ts +213 -0
- package/src/domain/yields.ts +189 -0
- package/src/facade/calls.ts +135 -0
- package/src/facade/chain-configs.ts +5 -0
- package/src/facade/flows.ts +35 -2
- package/src/facade/index.ts +3 -0
- package/src/facade/kasu.ts +32 -2
- package/src/facade/strategies.ts +98 -7
- package/src/facade/types.ts +19 -0
- package/src/flows/batch-errors.ts +118 -0
- package/src/flows/batch-submission.ts +511 -0
- package/src/flows/deposit-flow.ts +379 -7
- package/src/flows/index.ts +34 -1
- package/src/flows/withdraw-flow.ts +219 -10
- package/src/services/DataService/queries.ts +1 -0
- 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;
|