@logswap/sdk 0.5.55 → 0.5.57
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 +2 -75
- package/dist/actions.d.ts +1 -1
- package/dist/actions.js +1 -1
- package/dist/admin.d.ts +7 -7
- package/dist/admin.js +7 -7
- package/dist/claims.js +15 -34
- package/dist/client.d.ts +13 -6
- package/dist/client.js +74 -17
- package/dist/fpool.d.ts +53 -53
- package/dist/fpool.js +51 -51
- package/dist/keys.d.ts +1 -1
- package/dist/liquidity.d.ts +7 -7
- package/dist/liquidity.js +6 -6
- package/dist/onboard.d.ts +11 -0
- package/dist/onboard.js +23 -9
- package/dist/pools.d.ts +2 -2
- package/dist/pools.js +1 -1
- package/dist/write.d.ts +13 -1
- package/dist/write.js +26 -1
- package/package.json +3 -2
- package/src/actions.ts +1 -1
- package/src/admin.ts +8 -8
- package/src/claims.ts +14 -36
- package/src/client.ts +78 -19
- package/src/fpool.ts +68 -68
- package/src/keys.ts +1 -1
- package/src/liquidity.ts +8 -8
- package/src/onboard.ts +25 -8
- package/src/pools.ts +2 -2
- package/src/write.ts +27 -2
package/src/onboard.ts
CHANGED
|
@@ -22,6 +22,23 @@ export const MAX_UINT256 = (1n << 256n) - 1n;
|
|
|
22
22
|
export const MAX_UINT160 = (1n << 160n) - 1n;
|
|
23
23
|
export const MAX_UINT48 = (1n << 48n) - 1n;
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* How long a Permit2 allowance to the router lives: 30 days, as Uniswap's own interface does. It
|
|
27
|
+
* used to be `MAX_UINT48` — never — so a router bug or a compromised router could pull every
|
|
28
|
+
* approved token from every user indefinitely. A lapsed allowance is re-granted by one more
|
|
29
|
+
* signature, riding inside the next write ({@link permit2SigCalls}).
|
|
30
|
+
*/
|
|
31
|
+
export const PERMIT2_EXPIRY_SECONDS = 30n * 24n * 3600n;
|
|
32
|
+
/** An allowance that lapses within this window is treated as already lapsed: it could die in flight. */
|
|
33
|
+
const EXPIRY_MARGIN_SECONDS = 3600;
|
|
34
|
+
|
|
35
|
+
const nowSeconds = () => Math.floor(Date.now() / 1000);
|
|
36
|
+
/** The expiry to grant now. */
|
|
37
|
+
export const permit2Expiry = (): bigint => BigInt(nowSeconds()) + PERMIT2_EXPIRY_SECONDS;
|
|
38
|
+
/** Whether a Permit2 allowance (amount, expiration) covers `need` for long enough to land. */
|
|
39
|
+
export const permit2Covers = (amount: bigint, expiration: number, need: bigint): boolean =>
|
|
40
|
+
amount >= need && expiration > nowSeconds() + EXPIRY_MARGIN_SECONDS;
|
|
41
|
+
|
|
25
42
|
const ERC20 = parseAbi([
|
|
26
43
|
"function approve(address spender, uint256 amount) returns (bool)",
|
|
27
44
|
"function allowance(address owner, address spender) view returns (uint256)",
|
|
@@ -71,7 +88,7 @@ export async function approvePermit2ForRouter(
|
|
|
71
88
|
c: LogswapClient,
|
|
72
89
|
token: Address,
|
|
73
90
|
amount: bigint = MAX_UINT160,
|
|
74
|
-
expiration: bigint =
|
|
91
|
+
expiration: bigint = permit2Expiry(),
|
|
75
92
|
): Promise<Hash | null> {
|
|
76
93
|
if (!usesPermit2(c)) return null;
|
|
77
94
|
const { account } = requireWallet(c);
|
|
@@ -109,13 +126,13 @@ export async function onboardToken(c: LogswapClient, token: Address, need: bigin
|
|
|
109
126
|
}
|
|
110
127
|
|
|
111
128
|
if (usesPermit2(c)) {
|
|
112
|
-
const [amt] = (await c.public.readContract({
|
|
129
|
+
const [amt, expiration] = (await c.public.readContract({
|
|
113
130
|
address: c.addresses.permit2!,
|
|
114
131
|
abi: PERMIT2,
|
|
115
132
|
functionName: "allowance",
|
|
116
133
|
args: [address, token, c.addresses.router],
|
|
117
134
|
})) as readonly [bigint, number, number];
|
|
118
|
-
if (amt
|
|
135
|
+
if (!permit2Covers(amt, expiration, need)) {
|
|
119
136
|
const h = await approvePermit2ForRouter(c, token);
|
|
120
137
|
if (h) {
|
|
121
138
|
await c.public.waitForTransactionReceipt({ hash: h });
|
|
@@ -150,13 +167,13 @@ export async function checkTokenReadiness(
|
|
|
150
167
|
|
|
151
168
|
let needsPermit2Approval = false;
|
|
152
169
|
if (usesPermit2(c)) {
|
|
153
|
-
const [amt] = (await c.public.readContract({
|
|
170
|
+
const [amt, expiration] = (await c.public.readContract({
|
|
154
171
|
address: c.addresses.permit2!,
|
|
155
172
|
abi: PERMIT2,
|
|
156
173
|
functionName: "allowance",
|
|
157
174
|
args: [owner, token, c.addresses.router],
|
|
158
175
|
})) as readonly [bigint, number, number];
|
|
159
|
-
needsPermit2Approval = amt
|
|
176
|
+
needsPermit2Approval = !permit2Covers(amt, expiration, need);
|
|
160
177
|
}
|
|
161
178
|
|
|
162
179
|
return { usesPermit2: usesPermit2(c), needsTokenApproval: allowance < need, needsPermit2Approval };
|
|
@@ -195,15 +212,15 @@ export async function permit2SigCalls(c: LogswapClient, tokens: Address[]): Prom
|
|
|
195
212
|
const chainId = await c.public.getChainId();
|
|
196
213
|
const calls: Hex[] = [];
|
|
197
214
|
for (const token of [...new Set(tokens.map((t) => t.toLowerCase() as Address))]) {
|
|
198
|
-
const [amt, , nonce] = (await c.public.readContract({
|
|
215
|
+
const [amt, expiration, nonce] = (await c.public.readContract({
|
|
199
216
|
address: c.addresses.permit2!,
|
|
200
217
|
abi: PERMIT2,
|
|
201
218
|
functionName: "allowance",
|
|
202
219
|
args: [owner, token, c.addresses.router],
|
|
203
220
|
})) as readonly [bigint, number, number];
|
|
204
|
-
if (amt
|
|
221
|
+
if (permit2Covers(amt, expiration, 1n << 120n)) continue; // effectively unlimited, and not lapsing
|
|
205
222
|
const message = {
|
|
206
|
-
details: { token, amount: MAX_UINT160, expiration: Number(
|
|
223
|
+
details: { token, amount: MAX_UINT160, expiration: Number(permit2Expiry()), nonce },
|
|
207
224
|
spender: c.addresses.router,
|
|
208
225
|
sigDeadline: BigInt(Math.floor(Date.now() / 1000)) + 1800n,
|
|
209
226
|
};
|
package/src/pools.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface PoolState {
|
|
|
31
31
|
reserveQuote: bigint;
|
|
32
32
|
/** Fee-per-unit-L accumulator: $\phi \cdot \mathrm{TV}(\log p)$. */
|
|
33
33
|
F: bigint;
|
|
34
|
-
/** Quadratic variation of log-price, $\Sigma = \sum_{\text{swaps}} (\Delta x_{\text{swap}})^2$ — one term per swap
|
|
34
|
+
/** Quadratic variation of log-price, $\Sigma = \sum_{\text{swaps}} (\Delta x_{\text{swap}})^2$ — one term per swap. */
|
|
35
35
|
bigSigma: bigint;
|
|
36
36
|
currentK: bigint;
|
|
37
37
|
protocolOwed: bigint;
|
|
@@ -52,7 +52,7 @@ export async function getPool(c: LogswapClient, key: PoolKey): Promise<PoolState
|
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* The market's fee: the key's `phi` with `0n` resolved to `DEFAULT_FEE`. Constant for the market's
|
|
55
|
-
* life (C version 4,
|
|
55
|
+
* life (C version 4, — `phiEff` and the variance kernel before). Reverts for a key
|
|
56
56
|
* nobody initialized, which `discoverMarkets` relies on to drop a stale indexer's rows.
|
|
57
57
|
*/
|
|
58
58
|
export async function phiOf(c: LogswapClient, key: PoolKey): Promise<bigint> {
|
package/src/write.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* what you have — but the bound is what ships.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import type { Address, Hash } from "viem";
|
|
16
|
+
import type { Address, Hash, Hex } from "viem";
|
|
17
17
|
import type { LogswapClient } from "./client.js";
|
|
18
18
|
|
|
19
19
|
/** Disables the deadline check. Deliberate opt-out, never a default. */
|
|
@@ -114,6 +114,31 @@ export async function sendRouterWrite(
|
|
|
114
114
|
return c.wallet!.writeContract({ ...(params as object), gas } as never);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Whether an error is the user saying no in their wallet (EIP-1193 code 4001, viem's
|
|
119
|
+
* `UserRejectedRequestError`), however deep viem nested it.
|
|
120
|
+
*/
|
|
121
|
+
export function isUserRejection(e: unknown): boolean {
|
|
122
|
+
const seen = new Set<unknown>();
|
|
123
|
+
for (let x: unknown = e; x && typeof x === "object" && !seen.has(x); x = (x as { cause?: unknown }).cause) {
|
|
124
|
+
seen.add(x);
|
|
125
|
+
const o = x as { name?: string; code?: number };
|
|
126
|
+
if (o.name === "UserRejectedRequestError" || o.code === 4001) return true;
|
|
127
|
+
}
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The permit signatures are an optimisation: when they cannot be produced (a wallet without
|
|
133
|
+
* `signTypedData`, a read that failed) the write goes out plain and relies on a standing
|
|
134
|
+
* allowance. A REJECTED signature is not that — it was swallowed too, and the user who had just
|
|
135
|
+
* said no was shown a transaction prompt, which then reverted with a Permit2 error that hid why.
|
|
136
|
+
*/
|
|
137
|
+
export function fallBackUnlessRejected(e: unknown): Hex[] {
|
|
138
|
+
if (isUserRejection(e)) throw e;
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
|
|
117
142
|
/** Simulate a router write and return its decoded result, without sending. */
|
|
118
143
|
export async function simulateRouterWrite<T>(
|
|
119
144
|
c: LogswapClient,
|
|
@@ -142,7 +167,7 @@ export async function sendRouterWriteWithPermits(
|
|
|
142
167
|
pullTokens: Address[],
|
|
143
168
|
): Promise<Hash> {
|
|
144
169
|
const { permit2SigCalls } = await import("./onboard.js");
|
|
145
|
-
const sigCalls = await permit2SigCalls(c, pullTokens).catch(
|
|
170
|
+
const sigCalls = await permit2SigCalls(c, pullTokens).catch(fallBackUnlessRejected);
|
|
146
171
|
if (sigCalls.length === 0) return sendRouterWrite(c, args);
|
|
147
172
|
const { encodeFunctionData } = await import("viem");
|
|
148
173
|
const inner = encodeFunctionData({ abi: args.abi, functionName: args.functionName, args: args.args } as never);
|