@splitmarkets/sdk 0.2.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 +171 -0
- package/SKILL.md +73 -0
- package/dist/chunk-MZEPXYHI.js +758 -0
- package/dist/index.d.ts +527 -0
- package/dist/index.js +1 -0
- package/dist/widget.d.ts +43 -0
- package/dist/widget.js +272 -0
- package/package.json +38 -0
|
@@ -0,0 +1,758 @@
|
|
|
1
|
+
import { parseAbi, parseAbiItem, toHex, createPublicClient, http, keccak256, toBytes, encodeFunctionData, encodeAbiParameters, parseSignature, decodeEventLog, decodeAbiParameters } from 'viem';
|
|
2
|
+
import { base, arbitrum } from 'viem/chains';
|
|
3
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
4
|
+
|
|
5
|
+
// split.ts
|
|
6
|
+
var BASE_CHAIN_ID = 8453;
|
|
7
|
+
var ARB_CHAIN_ID = 42161;
|
|
8
|
+
function chainIdOf(chain) {
|
|
9
|
+
return chain === "arbitrum" ? ARB_CHAIN_ID : BASE_CHAIN_ID;
|
|
10
|
+
}
|
|
11
|
+
var FACILITATOR_URL_BASE = "https://bundlerfix---b402-facilitator-base-posmj54s5q-uc.a.run.app";
|
|
12
|
+
var FACILITATOR_URL_ARB = "https://b402-facilitator-arb-62092339396.us-central1.run.app";
|
|
13
|
+
function facilitatorUrlForChain(chain) {
|
|
14
|
+
return chain === "arbitrum" ? FACILITATOR_URL_ARB : FACILITATOR_URL_BASE;
|
|
15
|
+
}
|
|
16
|
+
var NEXUS_FACTORY = "0x0000006648ED9B2B842552BE63Af870bC74af837";
|
|
17
|
+
var NEXUS_BOOTSTRAP = "0x0000003eDf18913c01cBc482C978bBD3D6E8ffA3";
|
|
18
|
+
function encodeNexusInitData(owner) {
|
|
19
|
+
const validatorInitData = owner.toLowerCase();
|
|
20
|
+
const bootstrapCall = encodeFunctionData({
|
|
21
|
+
abi: parseAbi(["function initNexusWithDefaultValidator(bytes data)"]),
|
|
22
|
+
functionName: "initNexusWithDefaultValidator",
|
|
23
|
+
args: [validatorInitData]
|
|
24
|
+
});
|
|
25
|
+
return encodeAbiParameters(
|
|
26
|
+
[{ type: "address" }, { type: "bytes" }],
|
|
27
|
+
[NEXUS_BOOTSTRAP, bootstrapCall]
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
function getMasterWalletSalt(owner) {
|
|
31
|
+
const normalized = owner.toLowerCase().slice(2);
|
|
32
|
+
const reversed = normalized.split("").reverse().join("");
|
|
33
|
+
return BigInt(keccak256(toBytes(`b402-${reversed}`)));
|
|
34
|
+
}
|
|
35
|
+
var nexusFactoryAbi = parseAbi([
|
|
36
|
+
"function computeAccountAddress(bytes initData, bytes32 salt) view returns (address)"
|
|
37
|
+
]);
|
|
38
|
+
async function deriveSmartWallet(owner, chain, rpcUrl) {
|
|
39
|
+
const salt = getMasterWalletSalt(owner);
|
|
40
|
+
const smartWallet = await getPublic(chain, rpcUrl).readContract({
|
|
41
|
+
address: NEXUS_FACTORY,
|
|
42
|
+
abi: nexusFactoryAbi,
|
|
43
|
+
functionName: "computeAccountAddress",
|
|
44
|
+
args: [encodeNexusInitData(owner), toHex(salt, { size: 32 })]
|
|
45
|
+
});
|
|
46
|
+
return { smartWallet, salt: salt.toString() };
|
|
47
|
+
}
|
|
48
|
+
var INCOGNITO_RESERVED_MESSAGE = "b402 Incognito EOA Derivation";
|
|
49
|
+
var INCOGNITO_CACHE_KEY_PREFIX = "incognito_signature_v1_";
|
|
50
|
+
var INCOGNITO_CACHE_DURATION = 24 * 60 * 60 * 1e3;
|
|
51
|
+
var _sigMem = /* @__PURE__ */ new Map();
|
|
52
|
+
var _inFlightSig = /* @__PURE__ */ new Map();
|
|
53
|
+
function readCachedSig(cacheKey) {
|
|
54
|
+
const mem = _sigMem.get(cacheKey);
|
|
55
|
+
if (mem && Date.now() - mem.timestamp < INCOGNITO_CACHE_DURATION) return mem.signature;
|
|
56
|
+
if (typeof globalThis !== "undefined" && globalThis.sessionStorage) {
|
|
57
|
+
try {
|
|
58
|
+
const raw = globalThis.sessionStorage.getItem(cacheKey);
|
|
59
|
+
if (raw) {
|
|
60
|
+
const { signature, timestamp } = JSON.parse(raw);
|
|
61
|
+
if (signature && Date.now() - timestamp < INCOGNITO_CACHE_DURATION) return signature;
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return void 0;
|
|
67
|
+
}
|
|
68
|
+
function writeCachedSig(cacheKey, signature) {
|
|
69
|
+
_sigMem.set(cacheKey, { signature, timestamp: Date.now() });
|
|
70
|
+
if (typeof globalThis !== "undefined" && globalThis.sessionStorage) {
|
|
71
|
+
try {
|
|
72
|
+
globalThis.sessionStorage.setItem(
|
|
73
|
+
cacheKey,
|
|
74
|
+
JSON.stringify({ signature, timestamp: Date.now() })
|
|
75
|
+
);
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function getCachedSignature(walletClient, account) {
|
|
81
|
+
const cacheKey = `${INCOGNITO_CACHE_KEY_PREFIX}${account.toLowerCase()}`;
|
|
82
|
+
const cached = readCachedSig(cacheKey);
|
|
83
|
+
if (cached) return cached;
|
|
84
|
+
const existing = _inFlightSig.get(cacheKey);
|
|
85
|
+
if (existing) return existing;
|
|
86
|
+
const p = (async () => {
|
|
87
|
+
const signing = walletClient.account ?? account;
|
|
88
|
+
const signature = await walletClient.signMessage({
|
|
89
|
+
account: signing,
|
|
90
|
+
message: INCOGNITO_RESERVED_MESSAGE
|
|
91
|
+
});
|
|
92
|
+
writeCachedSig(cacheKey, signature);
|
|
93
|
+
return signature;
|
|
94
|
+
})();
|
|
95
|
+
_inFlightSig.set(cacheKey, p);
|
|
96
|
+
try {
|
|
97
|
+
return await p;
|
|
98
|
+
} finally {
|
|
99
|
+
_inFlightSig.delete(cacheKey);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function getIncognitoSigner(walletClient, account) {
|
|
103
|
+
const sig = await getCachedSignature(walletClient, account);
|
|
104
|
+
return privateKeyToAccount(keccak256(sig));
|
|
105
|
+
}
|
|
106
|
+
async function deriveTradingAccount(walletClient, account, chain = "base", rpcUrl) {
|
|
107
|
+
const signer = await getIncognitoSigner(walletClient, account);
|
|
108
|
+
const { smartWallet, salt } = await deriveSmartWallet(signer.address, chain, rpcUrl);
|
|
109
|
+
return {
|
|
110
|
+
eoa: account,
|
|
111
|
+
incognitoEOA: signer.address,
|
|
112
|
+
smartWallet,
|
|
113
|
+
salt,
|
|
114
|
+
// The 7579 validator authenticates the UserOp against the owner (incognito) key;
|
|
115
|
+
// signing the raw userOpHash with the in-memory account is SILENT.
|
|
116
|
+
signUserOpHash: (userOpHash) => signer.signMessage({ message: { raw: userOpHash } })
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
var approveAbi = parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
|
|
120
|
+
var transferAuthAbi = parseAbi([
|
|
121
|
+
"function transferWithAuthorization(address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s)"
|
|
122
|
+
]);
|
|
123
|
+
var tokenMetaAbi = parseAbi([
|
|
124
|
+
"function name() view returns (string)",
|
|
125
|
+
"function version() view returns (string)"
|
|
126
|
+
]);
|
|
127
|
+
var TRANSFER_WITH_AUTH_TYPES = {
|
|
128
|
+
TransferWithAuthorization: [
|
|
129
|
+
{ name: "from", type: "address" },
|
|
130
|
+
{ name: "to", type: "address" },
|
|
131
|
+
{ name: "value", type: "uint256" },
|
|
132
|
+
{ name: "validAfter", type: "uint256" },
|
|
133
|
+
{ name: "validBefore", type: "uint256" },
|
|
134
|
+
{ name: "nonce", type: "bytes32" }
|
|
135
|
+
]
|
|
136
|
+
};
|
|
137
|
+
async function buildEIP3009PullCall(args) {
|
|
138
|
+
const client = getPublic(args.chain, args.rpcUrl);
|
|
139
|
+
const [tokenName, tokenVersion] = await Promise.all([
|
|
140
|
+
client.readContract({ address: args.usdc, abi: tokenMetaAbi, functionName: "name" }),
|
|
141
|
+
client.readContract({ address: args.usdc, abi: tokenMetaAbi, functionName: "version" }).catch(
|
|
142
|
+
() => "2"
|
|
143
|
+
)
|
|
144
|
+
]);
|
|
145
|
+
const domain = {
|
|
146
|
+
name: tokenName,
|
|
147
|
+
version: tokenVersion,
|
|
148
|
+
chainId: chainIdOf(args.chain),
|
|
149
|
+
verifyingContract: args.usdc
|
|
150
|
+
};
|
|
151
|
+
const validAfter = 0n;
|
|
152
|
+
const validBefore = BigInt(Math.floor(Date.now() / 1e3) + 3600);
|
|
153
|
+
const nonce = toHex(crypto.getRandomValues(new Uint8Array(32)));
|
|
154
|
+
const message = {
|
|
155
|
+
from: args.owner,
|
|
156
|
+
to: args.to,
|
|
157
|
+
value: args.value.toString(),
|
|
158
|
+
validAfter: validAfter.toString(),
|
|
159
|
+
validBefore: validBefore.toString(),
|
|
160
|
+
nonce
|
|
161
|
+
};
|
|
162
|
+
const sig = await args.walletClient.signTypedData({
|
|
163
|
+
// ACCOUNT OBJECT not address string — local wallets sign typed data in-memory;
|
|
164
|
+
// a string forces a json-rpc account → eth_signTypedData_v4 routed to the HTTP
|
|
165
|
+
// transport (no such method), which breaks embedded wallets.
|
|
166
|
+
account: args.walletClient.account ?? args.owner,
|
|
167
|
+
domain,
|
|
168
|
+
types: TRANSFER_WITH_AUTH_TYPES,
|
|
169
|
+
primaryType: "TransferWithAuthorization",
|
|
170
|
+
// uint256 fields are decimal strings (JSON-safe); viem's generic widens here.
|
|
171
|
+
message
|
|
172
|
+
});
|
|
173
|
+
const { r, s, v, yParity } = parseSignature(sig);
|
|
174
|
+
const vByte = Number(v ?? BigInt(yParity + 27));
|
|
175
|
+
return {
|
|
176
|
+
to: args.usdc,
|
|
177
|
+
value: "0",
|
|
178
|
+
data: encodeFunctionData({
|
|
179
|
+
abi: transferAuthAbi,
|
|
180
|
+
functionName: "transferWithAuthorization",
|
|
181
|
+
args: [args.owner, args.to, args.value, validAfter, validBefore, nonce, vByte, r, s]
|
|
182
|
+
})
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
var splitBuyAbi = parseAbi([
|
|
186
|
+
"function buyN(uint256 seriesId, uint256 qN, uint256 maxCost, (address,uint256,uint256,uint256,uint256) q, bytes sig) returns (uint256)"
|
|
187
|
+
]);
|
|
188
|
+
var splitSellAbi = parseAbi([
|
|
189
|
+
"function sellN(uint256 seriesId, uint256 qN, uint256 minProceeds, (address,uint256,uint256,uint256,uint256) q, bytes sig) returns (uint256)"
|
|
190
|
+
]);
|
|
191
|
+
function buildSplitPoolBuyCalls(pool, usdc, seriesId, qN, total, quote) {
|
|
192
|
+
return [
|
|
193
|
+
{
|
|
194
|
+
to: usdc,
|
|
195
|
+
value: "0",
|
|
196
|
+
data: encodeFunctionData({ abi: approveAbi, functionName: "approve", args: [pool, total] })
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
to: pool,
|
|
200
|
+
value: "0",
|
|
201
|
+
data: encodeFunctionData({
|
|
202
|
+
abi: splitBuyAbi,
|
|
203
|
+
functionName: "buyN",
|
|
204
|
+
args: [BigInt(seriesId), qN, total, quoteTuple(quote), quote.signature]
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
];
|
|
208
|
+
}
|
|
209
|
+
function buildSplitPoolCloseCalls(pool, nToken, seriesId, qN, minProceeds, quote) {
|
|
210
|
+
return [
|
|
211
|
+
{
|
|
212
|
+
to: nToken,
|
|
213
|
+
value: "0",
|
|
214
|
+
data: encodeFunctionData({ abi: approveAbi, functionName: "approve", args: [pool, qN] })
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
to: pool,
|
|
218
|
+
value: "0",
|
|
219
|
+
data: encodeFunctionData({
|
|
220
|
+
abi: splitSellAbi,
|
|
221
|
+
functionName: "sellN",
|
|
222
|
+
args: [BigInt(seriesId), qN, minProceeds, quoteTuple(quote), quote.signature]
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
];
|
|
226
|
+
}
|
|
227
|
+
async function gaslessExecute(args) {
|
|
228
|
+
const apiBase = args.facilitatorUrl.replace(/\/+$/, "").replace(/\/api\/v1$/, "") + "/api/v1";
|
|
229
|
+
const verifyRes = await fetch(`${apiBase}/wallet/incognito/verify`, {
|
|
230
|
+
method: "POST",
|
|
231
|
+
headers: { "Content-Type": "application/json" },
|
|
232
|
+
body: JSON.stringify({
|
|
233
|
+
ownerEOA: args.owner,
|
|
234
|
+
walletAddress: args.walletAddress,
|
|
235
|
+
salt: args.salt,
|
|
236
|
+
calls: args.calls,
|
|
237
|
+
chainId: chainIdOf(args.chain)
|
|
238
|
+
})
|
|
239
|
+
});
|
|
240
|
+
if (!verifyRes.ok) {
|
|
241
|
+
throw new Error(`incognito/verify failed: ${verifyRes.status} ${await verifyRes.text()}`);
|
|
242
|
+
}
|
|
243
|
+
const verify = await verifyRes.json();
|
|
244
|
+
if (!verify.isValid || !verify.userOp || !verify.userOpHash) {
|
|
245
|
+
throw new Error(verify.invalidReason || "incognito/verify rejected the calls");
|
|
246
|
+
}
|
|
247
|
+
const signature = await args.signUserOpHash(verify.userOpHash);
|
|
248
|
+
const settleRes = await fetch(`${apiBase}/wallet/incognito/settle`, {
|
|
249
|
+
method: "POST",
|
|
250
|
+
headers: { "Content-Type": "application/json" },
|
|
251
|
+
body: JSON.stringify({ userOp: { ...verify.userOp, signature } })
|
|
252
|
+
});
|
|
253
|
+
if (!settleRes.ok) {
|
|
254
|
+
throw new Error(`incognito/settle failed: ${settleRes.status} ${await settleRes.text()}`);
|
|
255
|
+
}
|
|
256
|
+
const settle = await settleRes.json();
|
|
257
|
+
if (!settle.txHash) throw new Error(settle.errorReason || "incognito/settle did not confirm");
|
|
258
|
+
const txHash = settle.txHash;
|
|
259
|
+
const userOpHash = settle.userOpHash ?? verify.userOpHash;
|
|
260
|
+
await assertUserOpSucceeded(args.client, txHash, userOpHash);
|
|
261
|
+
return { txHash, userOpHash };
|
|
262
|
+
}
|
|
263
|
+
var ENTRY_POINT_07 = "0x0000000071727De22E5E9d8BAf0edAc6f37da032";
|
|
264
|
+
var ERROR_STRING_SELECTOR = "0x08c379a0";
|
|
265
|
+
var userOpEventAbi = parseAbiItem(
|
|
266
|
+
"event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed)"
|
|
267
|
+
);
|
|
268
|
+
var userOpRevertReasonAbi = parseAbiItem(
|
|
269
|
+
"event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason)"
|
|
270
|
+
);
|
|
271
|
+
function decodeRevertReason(revertReason) {
|
|
272
|
+
try {
|
|
273
|
+
if (revertReason && revertReason.toLowerCase().startsWith(ERROR_STRING_SELECTOR)) {
|
|
274
|
+
const [reason] = decodeAbiParameters([{ type: "string" }], `0x${revertReason.slice(10)}`);
|
|
275
|
+
if (reason) return reason;
|
|
276
|
+
}
|
|
277
|
+
} catch {
|
|
278
|
+
}
|
|
279
|
+
return "trade reverted on-chain";
|
|
280
|
+
}
|
|
281
|
+
async function assertUserOpSucceeded(client, txHash, userOpHash) {
|
|
282
|
+
const receipt = await client.waitForTransactionReceipt({ hash: txHash });
|
|
283
|
+
const wantHash = userOpHash.toLowerCase();
|
|
284
|
+
let ourEvent;
|
|
285
|
+
for (const log of receipt.logs) {
|
|
286
|
+
if (log.address.toLowerCase() !== ENTRY_POINT_07.toLowerCase()) continue;
|
|
287
|
+
try {
|
|
288
|
+
const decoded = decodeEventLog({ abi: [userOpEventAbi], data: log.data, topics: log.topics });
|
|
289
|
+
if (decoded.eventName === "UserOperationEvent" && decoded.args.userOpHash.toLowerCase() === wantHash) {
|
|
290
|
+
ourEvent = { success: decoded.args.success };
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (!ourEvent || ourEvent.success) return;
|
|
297
|
+
let reason = "trade reverted on-chain";
|
|
298
|
+
for (const log of receipt.logs) {
|
|
299
|
+
if (log.address.toLowerCase() !== ENTRY_POINT_07.toLowerCase()) continue;
|
|
300
|
+
try {
|
|
301
|
+
const decoded = decodeEventLog({ abi: [userOpRevertReasonAbi], data: log.data, topics: log.topics });
|
|
302
|
+
if (decoded.eventName === "UserOperationRevertReason" && decoded.args.userOpHash.toLowerCase() === wantHash) {
|
|
303
|
+
reason = decodeRevertReason(decoded.args.revertReason);
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
} catch {
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
throw new Error(`Trade reverted: ${reason}`);
|
|
310
|
+
}
|
|
311
|
+
var ONE = 10n ** 18n;
|
|
312
|
+
var toNWei = (qN) => BigInt(Math.round(qN * 1e18));
|
|
313
|
+
var quoteCost = (qNWei, perN) => (qNWei * perN + ONE - 1n) / ONE;
|
|
314
|
+
var sellProceeds = (qNWei, perN) => qNWei * perN / ONE;
|
|
315
|
+
function defaultFeeHeadroom(amount) {
|
|
316
|
+
const fivePct = amount * 5n / 100n;
|
|
317
|
+
const floor = 50000n;
|
|
318
|
+
return fivePct > floor ? fivePct : floor;
|
|
319
|
+
}
|
|
320
|
+
async function buyGasless(args) {
|
|
321
|
+
const { chain, side, seriesId } = args;
|
|
322
|
+
const pool = poolAddress(chain, side);
|
|
323
|
+
const usdc = CHAINS[chain].usdc;
|
|
324
|
+
const client = getPublic(chain, args.rpcUrl);
|
|
325
|
+
const trading = args.trading ?? await deriveTradingAccount(args.walletClient, args.account, chain, args.rpcUrl);
|
|
326
|
+
const quote = args.quote ?? await getQuote(chain, side, seriesId, args.apiUrl);
|
|
327
|
+
const qNWei = toNWei(args.qN);
|
|
328
|
+
const feeBps = await client.readContract({
|
|
329
|
+
address: pool,
|
|
330
|
+
abi: SPLIT_POOL_ABI,
|
|
331
|
+
functionName: "feeBps"
|
|
332
|
+
});
|
|
333
|
+
const cost = quoteCost(qNWei, BigInt(quote.askPerN));
|
|
334
|
+
const fee = cost * BigInt(feeBps) / 10000n;
|
|
335
|
+
const baseCost = cost + fee;
|
|
336
|
+
const slipBps = BigInt(args.slippageBps ?? 100);
|
|
337
|
+
const maxCost = baseCost + baseCost * slipBps / 10000n;
|
|
338
|
+
const grossNeed = maxCost + defaultFeeHeadroom(maxCost);
|
|
339
|
+
const smartBal = await client.readContract({
|
|
340
|
+
address: usdc,
|
|
341
|
+
abi: ERC20_ABI,
|
|
342
|
+
functionName: "balanceOf",
|
|
343
|
+
args: [trading.smartWallet]
|
|
344
|
+
});
|
|
345
|
+
const pullValue = grossNeed > smartBal ? grossNeed - smartBal : 0n;
|
|
346
|
+
if (pullValue > 0n) {
|
|
347
|
+
const eoaBal = await client.readContract({
|
|
348
|
+
address: usdc,
|
|
349
|
+
abi: ERC20_ABI,
|
|
350
|
+
functionName: "balanceOf",
|
|
351
|
+
args: [args.account]
|
|
352
|
+
});
|
|
353
|
+
if (eoaBal < pullValue) {
|
|
354
|
+
const need = (Number(pullValue) / 1e6).toFixed(2);
|
|
355
|
+
const have = (Number(eoaBal) / 1e6).toFixed(2);
|
|
356
|
+
throw new Error(`Insufficient USDC: need $${need} more, have $${have}.`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const calls = [];
|
|
360
|
+
if (pullValue > 0n) {
|
|
361
|
+
calls.push(
|
|
362
|
+
await buildEIP3009PullCall({
|
|
363
|
+
walletClient: args.walletClient,
|
|
364
|
+
owner: args.account,
|
|
365
|
+
to: trading.smartWallet,
|
|
366
|
+
value: pullValue,
|
|
367
|
+
usdc,
|
|
368
|
+
chain,
|
|
369
|
+
rpcUrl: args.rpcUrl
|
|
370
|
+
})
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
calls.push(...buildSplitPoolBuyCalls(pool, usdc, seriesId, qNWei, maxCost, quote));
|
|
374
|
+
return gaslessExecute({
|
|
375
|
+
facilitatorUrl: args.facilitatorUrl ?? facilitatorUrlForChain(chain),
|
|
376
|
+
walletAddress: trading.smartWallet,
|
|
377
|
+
owner: trading.incognitoEOA,
|
|
378
|
+
salt: trading.salt,
|
|
379
|
+
calls,
|
|
380
|
+
signUserOpHash: trading.signUserOpHash,
|
|
381
|
+
chain,
|
|
382
|
+
client
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
async function closeGasless(args) {
|
|
386
|
+
const { chain, side, seriesId } = args;
|
|
387
|
+
const pool = poolAddress(chain, side);
|
|
388
|
+
const client = getPublic(chain, args.rpcUrl);
|
|
389
|
+
const trading = args.trading ?? await deriveTradingAccount(args.walletClient, args.account, chain, args.rpcUrl);
|
|
390
|
+
const quote = args.quote ?? await getQuote(chain, side, seriesId, args.apiUrl);
|
|
391
|
+
const nToken = await client.readContract({
|
|
392
|
+
address: quote.vault,
|
|
393
|
+
abi: SPLIT_VAULT_ABI,
|
|
394
|
+
functionName: "N"
|
|
395
|
+
});
|
|
396
|
+
const bal = await client.readContract({
|
|
397
|
+
address: nToken,
|
|
398
|
+
abi: ERC20_ABI,
|
|
399
|
+
functionName: "balanceOf",
|
|
400
|
+
args: [trading.smartWallet]
|
|
401
|
+
});
|
|
402
|
+
const qNWei = args.qN !== void 0 ? toNWei(args.qN) : bal;
|
|
403
|
+
if (qNWei <= 0n) throw new Error("close: nothing to close in the trading account");
|
|
404
|
+
if (qNWei > bal) throw new Error("close: qN exceeds the trading account's balance");
|
|
405
|
+
const feeBps = await client.readContract({
|
|
406
|
+
address: pool,
|
|
407
|
+
abi: SPLIT_POOL_ABI,
|
|
408
|
+
functionName: "feeBps"
|
|
409
|
+
});
|
|
410
|
+
const proceeds = sellProceeds(qNWei, BigInt(quote.bidPerN));
|
|
411
|
+
const fee = proceeds * BigInt(feeBps) / 10000n;
|
|
412
|
+
const net = proceeds - fee;
|
|
413
|
+
const slipBps = BigInt(args.slippageBps ?? 100);
|
|
414
|
+
const minProceeds = net - net * slipBps / 10000n;
|
|
415
|
+
const calls = buildSplitPoolCloseCalls(pool, nToken, seriesId, qNWei, minProceeds, quote);
|
|
416
|
+
return gaslessExecute({
|
|
417
|
+
facilitatorUrl: args.facilitatorUrl ?? facilitatorUrlForChain(chain),
|
|
418
|
+
walletAddress: trading.smartWallet,
|
|
419
|
+
owner: trading.incognitoEOA,
|
|
420
|
+
salt: trading.salt,
|
|
421
|
+
calls,
|
|
422
|
+
signUserOpHash: trading.signUserOpHash,
|
|
423
|
+
chain,
|
|
424
|
+
client
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
async function getPositionGasless(args) {
|
|
428
|
+
const { chain, side, seriesId } = args;
|
|
429
|
+
const pool = poolAddress(chain, side);
|
|
430
|
+
const client = getPublic(chain, args.rpcUrl);
|
|
431
|
+
const trading = args.trading ?? await deriveTradingAccount(args.walletClient, args.account, chain, args.rpcUrl);
|
|
432
|
+
const [epoch, spot6] = await Promise.all([
|
|
433
|
+
client.readContract({ address: pool, abi: SPLIT_POOL_ABI, functionName: "epoch" }),
|
|
434
|
+
client.readContract({ address: pool, abi: SPLIT_POOL_ABI, functionName: "spot6" }).catch(() => 0n)
|
|
435
|
+
]);
|
|
436
|
+
const spot = Number(spot6) / 1e6;
|
|
437
|
+
const [vault, strikeRaw] = await client.readContract({
|
|
438
|
+
address: pool,
|
|
439
|
+
abi: SPLIT_POOL_ABI,
|
|
440
|
+
functionName: "seriesAt",
|
|
441
|
+
args: [epoch, BigInt(seriesId)]
|
|
442
|
+
});
|
|
443
|
+
const strike = Number(strikeRaw) / 1e6;
|
|
444
|
+
const nToken = await client.readContract({
|
|
445
|
+
address: vault,
|
|
446
|
+
abi: SPLIT_VAULT_ABI,
|
|
447
|
+
functionName: "N"
|
|
448
|
+
});
|
|
449
|
+
const bal = await client.readContract({
|
|
450
|
+
address: nToken,
|
|
451
|
+
abi: ERC20_ABI,
|
|
452
|
+
functionName: "balanceOf",
|
|
453
|
+
args: [trading.smartWallet]
|
|
454
|
+
});
|
|
455
|
+
const qN = Number(bal) / 1e18;
|
|
456
|
+
if (qN === 0) return { smartWallet: trading.smartWallet, qN: 0, markUsd: 0, intrinsicUsd: 0 };
|
|
457
|
+
const intrinsicPerN = side === "short" ? Math.max(0, strike - spot) : Math.max(0, spot - strike);
|
|
458
|
+
const intrinsicUsd = intrinsicPerN * qN;
|
|
459
|
+
let markUsd = intrinsicUsd;
|
|
460
|
+
try {
|
|
461
|
+
const q = await getQuote(chain, side, seriesId, args.apiUrl);
|
|
462
|
+
const ask = Number(q.askPerN) / 1e6;
|
|
463
|
+
const bid = Number(q.bidPerN) / 1e6;
|
|
464
|
+
const mid = ask > 0 && bid > 0 ? (ask + bid) / 2 : bid;
|
|
465
|
+
if (mid > 0) markUsd = mid * qN;
|
|
466
|
+
} catch {
|
|
467
|
+
}
|
|
468
|
+
return { smartWallet: trading.smartWallet, qN, markUsd, intrinsicUsd };
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// split.ts
|
|
472
|
+
var QUOTE_API = "https://quote-api-production-b5b9.up.railway.app";
|
|
473
|
+
var CHAINS = {
|
|
474
|
+
base: {
|
|
475
|
+
id: 8453,
|
|
476
|
+
name: "base",
|
|
477
|
+
splitPool: "0xe310f78e2721a2a10faabf8df830140756fcb579",
|
|
478
|
+
splitPoolPut: "0x8a176e1a7103cccbd6551eef65bc00da641a1ecb",
|
|
479
|
+
weth: "0x4200000000000000000000000000000000000006",
|
|
480
|
+
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
481
|
+
rpc: "https://base-rpc.publicnode.com"
|
|
482
|
+
},
|
|
483
|
+
arbitrum: {
|
|
484
|
+
id: 42161,
|
|
485
|
+
name: "arbitrum",
|
|
486
|
+
splitPool: "0xa1b161ff5ddd2437ba9e359397e868ef4cb6df2f",
|
|
487
|
+
splitPoolPut: "",
|
|
488
|
+
// calls only on Arbitrum for now
|
|
489
|
+
weth: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
490
|
+
usdc: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
491
|
+
rpc: "https://arbitrum-one-rpc.publicnode.com"
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
function cfg(chain) {
|
|
495
|
+
const c = CHAINS[chain];
|
|
496
|
+
if (!c) throw new Error(`split: unknown chain "${chain}" (use "base" | "arbitrum")`);
|
|
497
|
+
return c;
|
|
498
|
+
}
|
|
499
|
+
function poolAddress(chain, side) {
|
|
500
|
+
const c = cfg(chain);
|
|
501
|
+
if (side === "short") {
|
|
502
|
+
if (!c.splitPoolPut) throw new Error(`split: shorts (puts) not deployed on ${chain}`);
|
|
503
|
+
return c.splitPoolPut;
|
|
504
|
+
}
|
|
505
|
+
return c.splitPool;
|
|
506
|
+
}
|
|
507
|
+
var SPLIT_POOL_ABI = parseAbi([
|
|
508
|
+
"function epoch() view returns (uint256)",
|
|
509
|
+
"function phase() view returns (uint8)",
|
|
510
|
+
// 0 = Idle, 1 = Trading
|
|
511
|
+
"function seriesCount(uint256 epoch) view returns (uint256)",
|
|
512
|
+
"function seriesAt(uint256 epoch, uint256 id) view returns (address vault, uint256 strike)",
|
|
513
|
+
"function freeWeth() view returns (uint256)",
|
|
514
|
+
"function freeUsdc() view returns (uint256)",
|
|
515
|
+
"function spot6() view returns (uint256)",
|
|
516
|
+
"function feeBps() view returns (uint16)",
|
|
517
|
+
"function costWithFee(uint256 qN, uint256 askPerN) view returns (uint256)",
|
|
518
|
+
"function buyN(uint256 seriesId, uint256 qN, uint256 maxCost, (address,uint256,uint256,uint256,uint256) q, bytes sig) returns (uint256)",
|
|
519
|
+
"function sellN(uint256 seriesId, uint256 qN, uint256 minProceeds, (address,uint256,uint256,uint256,uint256) q, bytes sig) returns (uint256)"
|
|
520
|
+
]);
|
|
521
|
+
var SPLIT_VAULT_ABI = parseAbi([
|
|
522
|
+
"function strike() view returns (uint256)",
|
|
523
|
+
"function maturity() view returns (uint256)",
|
|
524
|
+
"function exerciseEnd() view returns (uint256)",
|
|
525
|
+
"function settled() view returns (bool)",
|
|
526
|
+
"function N() view returns (address)"
|
|
527
|
+
]);
|
|
528
|
+
var ERC20_ABI = parseAbi([
|
|
529
|
+
"function balanceOf(address) view returns (uint256)",
|
|
530
|
+
"function approve(address spender, uint256 amount) returns (bool)",
|
|
531
|
+
"function allowance(address owner, address spender) view returns (uint256)"
|
|
532
|
+
]);
|
|
533
|
+
var ONE2 = 10n ** 18n;
|
|
534
|
+
var _publics = /* @__PURE__ */ new Map();
|
|
535
|
+
function getPublic(chain, rpcUrl) {
|
|
536
|
+
const c = cfg(chain);
|
|
537
|
+
const url = rpcUrl ?? c.rpc;
|
|
538
|
+
const key = `${chain}:${url}`;
|
|
539
|
+
let p = _publics.get(key);
|
|
540
|
+
if (!p) {
|
|
541
|
+
p = createPublicClient({
|
|
542
|
+
chain: chain === "base" ? base : arbitrum,
|
|
543
|
+
transport: http(url),
|
|
544
|
+
batch: { multicall: { wait: 40 } }
|
|
545
|
+
});
|
|
546
|
+
_publics.set(key, p);
|
|
547
|
+
}
|
|
548
|
+
return p;
|
|
549
|
+
}
|
|
550
|
+
var chainObj = (chain) => chain === "base" ? base : arbitrum;
|
|
551
|
+
var toNWei2 = (qN) => BigInt(Math.round(qN * 1e18));
|
|
552
|
+
var quoteCost2 = (qNWei, perN) => (qNWei * perN + ONE2 - 1n) / ONE2;
|
|
553
|
+
var sellProceeds2 = (qNWei, perN) => qNWei * perN / ONE2;
|
|
554
|
+
function fmtUsd(n) {
|
|
555
|
+
return n >= 1e3 ? n.toLocaleString("en-US", { maximumFractionDigits: 0 }) : n.toFixed(0);
|
|
556
|
+
}
|
|
557
|
+
async function getMarkets(chain, side, rpcUrl) {
|
|
558
|
+
const pool = poolAddress(chain, side);
|
|
559
|
+
const pub = getPublic(chain, rpcUrl);
|
|
560
|
+
const [epoch, phaseRaw, spot6] = await Promise.all([
|
|
561
|
+
pub.readContract({ address: pool, abi: SPLIT_POOL_ABI, functionName: "epoch" }),
|
|
562
|
+
pub.readContract({ address: pool, abi: SPLIT_POOL_ABI, functionName: "phase" }),
|
|
563
|
+
pub.readContract({ address: pool, abi: SPLIT_POOL_ABI, functionName: "spot6" }).catch(() => 0n)
|
|
564
|
+
]);
|
|
565
|
+
if (Number(phaseRaw) !== 1) return [];
|
|
566
|
+
const spot = Number(spot6) / 1e6;
|
|
567
|
+
const count = await pub.readContract({
|
|
568
|
+
address: pool,
|
|
569
|
+
abi: SPLIT_POOL_ABI,
|
|
570
|
+
functionName: "seriesCount",
|
|
571
|
+
args: [epoch]
|
|
572
|
+
});
|
|
573
|
+
const n = Number(count);
|
|
574
|
+
if (n === 0) return [];
|
|
575
|
+
const rows = await Promise.all(
|
|
576
|
+
Array.from({ length: n }, async (_, id) => {
|
|
577
|
+
try {
|
|
578
|
+
const [vault, strikeRaw] = await pub.readContract({
|
|
579
|
+
address: pool,
|
|
580
|
+
abi: SPLIT_POOL_ABI,
|
|
581
|
+
functionName: "seriesAt",
|
|
582
|
+
args: [epoch, BigInt(id)]
|
|
583
|
+
});
|
|
584
|
+
const strike = Number(strikeRaw) / 1e6;
|
|
585
|
+
const q = await getQuote(chain, side, id);
|
|
586
|
+
const premium = Number(q.askPerN) / 1e6;
|
|
587
|
+
const leverage = premium > 0 ? spot / premium : 0;
|
|
588
|
+
const kind = side === "short" ? "put" : "call";
|
|
589
|
+
const label = leverage > 0 ? `${leverage.toFixed(0)}x \xB7 $${fmtUsd(strike)} ${kind}` : `$${fmtUsd(strike)} ${kind}`;
|
|
590
|
+
return { seriesId: id, vault, strike, leverage, spot, premium, label, side };
|
|
591
|
+
} catch {
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
})
|
|
595
|
+
);
|
|
596
|
+
return rows.filter((r) => r !== null);
|
|
597
|
+
}
|
|
598
|
+
async function getQuote(chain, side, seriesId, apiUrl = QUOTE_API) {
|
|
599
|
+
const pool = poolAddress(chain, side);
|
|
600
|
+
const put = side === "short" ? "&put=1" : "";
|
|
601
|
+
const url = `${apiUrl.replace(/\/+$/, "")}/pool-quote?pool=${pool}&series=${seriesId}${put}`;
|
|
602
|
+
const res = await fetch(url);
|
|
603
|
+
if (!res.ok) throw new Error(`split quote ${res.status}: ${await res.text()}`);
|
|
604
|
+
const j = await res.json();
|
|
605
|
+
if (j.error) throw new Error(`split quote: ${j.error}`);
|
|
606
|
+
if (!j.signature || !j.askPerN || !j.vault) throw new Error("split quote: malformed response");
|
|
607
|
+
return j;
|
|
608
|
+
}
|
|
609
|
+
function quoteTuple(q) {
|
|
610
|
+
return [q.vault, BigInt(q.askPerN), BigInt(q.bidPerN), BigInt(q.validUntil), BigInt(q.nonce)];
|
|
611
|
+
}
|
|
612
|
+
async function buy(args) {
|
|
613
|
+
const { chain, side, seriesId, account, walletClient } = args;
|
|
614
|
+
const pool = poolAddress(chain, side);
|
|
615
|
+
const c = cfg(chain);
|
|
616
|
+
const pub = getPublic(chain, args.rpcUrl);
|
|
617
|
+
const quote = args.quote ?? await getQuote(chain, side, seriesId);
|
|
618
|
+
const qNWei = toNWei2(args.qN);
|
|
619
|
+
const askPerN = BigInt(quote.askPerN);
|
|
620
|
+
const feeBps = await pub.readContract({
|
|
621
|
+
address: pool,
|
|
622
|
+
abi: SPLIT_POOL_ABI,
|
|
623
|
+
functionName: "feeBps"
|
|
624
|
+
});
|
|
625
|
+
const cost = quoteCost2(qNWei, askPerN);
|
|
626
|
+
const fee = cost * BigInt(feeBps) / 10000n;
|
|
627
|
+
const base2 = cost + fee;
|
|
628
|
+
const slipBps = BigInt(args.slippageBps ?? 100);
|
|
629
|
+
const maxCost = base2 + base2 * slipBps / 10000n;
|
|
630
|
+
const balance = await pub.readContract({
|
|
631
|
+
address: c.usdc,
|
|
632
|
+
abi: ERC20_ABI,
|
|
633
|
+
functionName: "balanceOf",
|
|
634
|
+
args: [account]
|
|
635
|
+
});
|
|
636
|
+
if (balance < maxCost) {
|
|
637
|
+
const need = (Number(maxCost) / 1e6).toFixed(2);
|
|
638
|
+
const have = (Number(balance) / 1e6).toFixed(2);
|
|
639
|
+
throw new Error(`Insufficient USDC: need $${need}, have $${have}.`);
|
|
640
|
+
}
|
|
641
|
+
await ensureAllowance(pub, walletClient, chain, c.usdc, account, pool, maxCost);
|
|
642
|
+
const txHash = await walletClient.writeContract({
|
|
643
|
+
address: pool,
|
|
644
|
+
abi: SPLIT_POOL_ABI,
|
|
645
|
+
functionName: "buyN",
|
|
646
|
+
args: [BigInt(seriesId), qNWei, maxCost, quoteTuple(quote), quote.signature],
|
|
647
|
+
account,
|
|
648
|
+
chain: chainObj(chain),
|
|
649
|
+
gas: 600000n
|
|
650
|
+
});
|
|
651
|
+
await pub.waitForTransactionReceipt({ hash: txHash });
|
|
652
|
+
return { txHash, qN: args.qN };
|
|
653
|
+
}
|
|
654
|
+
async function close(args) {
|
|
655
|
+
const { chain, side, seriesId, account, walletClient } = args;
|
|
656
|
+
const pool = poolAddress(chain, side);
|
|
657
|
+
const pub = getPublic(chain, args.rpcUrl);
|
|
658
|
+
const quote = args.quote ?? await getQuote(chain, side, seriesId);
|
|
659
|
+
const nToken = await pub.readContract({
|
|
660
|
+
address: quote.vault,
|
|
661
|
+
abi: SPLIT_VAULT_ABI,
|
|
662
|
+
functionName: "N"
|
|
663
|
+
});
|
|
664
|
+
const bal = await pub.readContract({
|
|
665
|
+
address: nToken,
|
|
666
|
+
abi: ERC20_ABI,
|
|
667
|
+
functionName: "balanceOf",
|
|
668
|
+
args: [account]
|
|
669
|
+
});
|
|
670
|
+
const qNWei = args.qN !== void 0 ? toNWei2(args.qN) : bal;
|
|
671
|
+
if (qNWei <= 0n) throw new Error("split close: nothing to close");
|
|
672
|
+
if (qNWei > bal) throw new Error("split close: qN exceeds your balance");
|
|
673
|
+
const feeBps = await pub.readContract({
|
|
674
|
+
address: pool,
|
|
675
|
+
abi: SPLIT_POOL_ABI,
|
|
676
|
+
functionName: "feeBps"
|
|
677
|
+
});
|
|
678
|
+
const proceeds = sellProceeds2(qNWei, BigInt(quote.bidPerN));
|
|
679
|
+
const fee = proceeds * BigInt(feeBps) / 10000n;
|
|
680
|
+
const net = proceeds - fee;
|
|
681
|
+
const slipBps = BigInt(args.slippageBps ?? 100);
|
|
682
|
+
const minProceeds = net - net * slipBps / 10000n;
|
|
683
|
+
await ensureAllowance(pub, walletClient, chain, nToken, account, pool, qNWei);
|
|
684
|
+
const txHash = await walletClient.writeContract({
|
|
685
|
+
address: pool,
|
|
686
|
+
abi: SPLIT_POOL_ABI,
|
|
687
|
+
functionName: "sellN",
|
|
688
|
+
args: [BigInt(seriesId), qNWei, minProceeds, quoteTuple(quote), quote.signature],
|
|
689
|
+
account,
|
|
690
|
+
chain: chainObj(chain),
|
|
691
|
+
gas: 600000n
|
|
692
|
+
});
|
|
693
|
+
await pub.waitForTransactionReceipt({ hash: txHash });
|
|
694
|
+
return { txHash, qN: Number(qNWei) / 1e18 };
|
|
695
|
+
}
|
|
696
|
+
async function getPosition(args) {
|
|
697
|
+
const { chain, side, seriesId, account } = args;
|
|
698
|
+
const pool = poolAddress(chain, side);
|
|
699
|
+
const pub = getPublic(chain, args.rpcUrl);
|
|
700
|
+
const [epoch, spot6] = await Promise.all([
|
|
701
|
+
pub.readContract({ address: pool, abi: SPLIT_POOL_ABI, functionName: "epoch" }),
|
|
702
|
+
pub.readContract({ address: pool, abi: SPLIT_POOL_ABI, functionName: "spot6" }).catch(() => 0n)
|
|
703
|
+
]);
|
|
704
|
+
const spot = Number(spot6) / 1e6;
|
|
705
|
+
const [vault, strikeRaw] = await pub.readContract({
|
|
706
|
+
address: pool,
|
|
707
|
+
abi: SPLIT_POOL_ABI,
|
|
708
|
+
functionName: "seriesAt",
|
|
709
|
+
args: [epoch, BigInt(seriesId)]
|
|
710
|
+
});
|
|
711
|
+
const strike = Number(strikeRaw) / 1e6;
|
|
712
|
+
const nToken = await pub.readContract({
|
|
713
|
+
address: vault,
|
|
714
|
+
abi: SPLIT_VAULT_ABI,
|
|
715
|
+
functionName: "N"
|
|
716
|
+
});
|
|
717
|
+
const bal = await pub.readContract({
|
|
718
|
+
address: nToken,
|
|
719
|
+
abi: ERC20_ABI,
|
|
720
|
+
functionName: "balanceOf",
|
|
721
|
+
args: [account]
|
|
722
|
+
});
|
|
723
|
+
const qN = Number(bal) / 1e18;
|
|
724
|
+
if (qN === 0) return { qN: 0, markUsd: 0, intrinsicUsd: 0 };
|
|
725
|
+
const intrinsicPerN = side === "short" ? Math.max(0, strike - spot) : Math.max(0, spot - strike);
|
|
726
|
+
const intrinsicUsd = intrinsicPerN * qN;
|
|
727
|
+
let markUsd = intrinsicUsd;
|
|
728
|
+
try {
|
|
729
|
+
const q = await getQuote(chain, side, seriesId);
|
|
730
|
+
const ask = Number(q.askPerN) / 1e6;
|
|
731
|
+
const bid = Number(q.bidPerN) / 1e6;
|
|
732
|
+
const mid = ask > 0 && bid > 0 ? (ask + bid) / 2 : bid;
|
|
733
|
+
if (mid > 0) markUsd = mid * qN;
|
|
734
|
+
} catch {
|
|
735
|
+
}
|
|
736
|
+
return { qN, markUsd, intrinsicUsd };
|
|
737
|
+
}
|
|
738
|
+
async function ensureAllowance(pub, walletClient, chain, token, owner, spender, amount) {
|
|
739
|
+
const allowance = await pub.readContract({
|
|
740
|
+
address: token,
|
|
741
|
+
abi: ERC20_ABI,
|
|
742
|
+
functionName: "allowance",
|
|
743
|
+
args: [owner, spender]
|
|
744
|
+
});
|
|
745
|
+
if (allowance >= amount) return;
|
|
746
|
+
const hash = await walletClient.writeContract({
|
|
747
|
+
address: token,
|
|
748
|
+
abi: ERC20_ABI,
|
|
749
|
+
functionName: "approve",
|
|
750
|
+
args: [spender, amount],
|
|
751
|
+
account: owner,
|
|
752
|
+
chain: chainObj(chain),
|
|
753
|
+
gas: 80000n
|
|
754
|
+
});
|
|
755
|
+
await pub.waitForTransactionReceipt({ hash });
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
export { ARB_CHAIN_ID, BASE_CHAIN_ID, CHAINS, ERC20_ABI, FACILITATOR_URL_ARB, FACILITATOR_URL_BASE, NEXUS_BOOTSTRAP, NEXUS_FACTORY, QUOTE_API, SPLIT_POOL_ABI, SPLIT_VAULT_ABI, buy, buyGasless, close, closeGasless, deriveSmartWallet, deriveTradingAccount, facilitatorUrlForChain, getMarkets, getPosition, getPositionGasless, getPublic, getQuote, poolAddress, quoteTuple };
|