@splitmarkets/sdk 0.2.0 → 0.2.2

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.
@@ -319,6 +319,7 @@ function defaultFeeHeadroom(amount) {
319
319
  }
320
320
  async function buyGasless(args) {
321
321
  const { chain, side, seriesId } = args;
322
+ await ensureWalletChain(args.walletClient, chain);
322
323
  const pool = poolAddress(chain, side);
323
324
  const usdc = CHAINS[chain].usdc;
324
325
  const client = getPublic(chain, args.rpcUrl);
@@ -384,6 +385,7 @@ async function buyGasless(args) {
384
385
  }
385
386
  async function closeGasless(args) {
386
387
  const { chain, side, seriesId } = args;
388
+ await ensureWalletChain(args.walletClient, chain);
387
389
  const pool = poolAddress(chain, side);
388
390
  const client = getPublic(chain, args.rpcUrl);
389
391
  const trading = args.trading ?? await deriveTradingAccount(args.walletClient, args.account, chain, args.rpcUrl);
@@ -548,6 +550,28 @@ function getPublic(chain, rpcUrl) {
548
550
  return p;
549
551
  }
550
552
  var chainObj = (chain) => chain === "base" ? base : arbitrum;
553
+ async function ensureWalletChain(walletClient, chain) {
554
+ if (walletClient.account?.type === "local") return;
555
+ const want = CHAINS[chain].id;
556
+ let current;
557
+ try {
558
+ current = await walletClient.getChainId();
559
+ } catch {
560
+ return;
561
+ }
562
+ if (current === want) return;
563
+ try {
564
+ await walletClient.switchChain({ id: want });
565
+ } catch (e) {
566
+ const code = e?.code;
567
+ if (code === 4902 || /unrecognized|not been added/i.test(String(e?.message))) {
568
+ await walletClient.addChain({ chain: chainObj(chain) });
569
+ await walletClient.switchChain({ id: want });
570
+ } else {
571
+ throw e;
572
+ }
573
+ }
574
+ }
551
575
  var toNWei2 = (qN) => BigInt(Math.round(qN * 1e18));
552
576
  var quoteCost2 = (qNWei, perN) => (qNWei * perN + ONE2 - 1n) / ONE2;
553
577
  var sellProceeds2 = (qNWei, perN) => qNWei * perN / ONE2;
@@ -611,6 +635,7 @@ function quoteTuple(q) {
611
635
  }
612
636
  async function buy(args) {
613
637
  const { chain, side, seriesId, account, walletClient } = args;
638
+ await ensureWalletChain(walletClient, chain);
614
639
  const pool = poolAddress(chain, side);
615
640
  const c = cfg(chain);
616
641
  const pub = getPublic(chain, args.rpcUrl);
@@ -653,6 +678,7 @@ async function buy(args) {
653
678
  }
654
679
  async function close(args) {
655
680
  const { chain, side, seriesId, account, walletClient } = args;
681
+ await ensureWalletChain(walletClient, chain);
656
682
  const pool = poolAddress(chain, side);
657
683
  const pub = getPublic(chain, args.rpcUrl);
658
684
  const quote = args.quote ?? await getQuote(chain, side, seriesId);
@@ -755,4 +781,4 @@ async function ensureAllowance(pub, walletClient, chain, token, owner, spender,
755
781
  await pub.waitForTransactionReceipt({ hash });
756
782
  }
757
783
 
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 };
784
+ 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, ensureWalletChain, facilitatorUrlForChain, getMarkets, getPosition, getPositionGasless, getPublic, getQuote, poolAddress, quoteTuple };
package/dist/index.d.ts CHANGED
@@ -446,6 +446,13 @@ interface TradeResult {
446
446
  }
447
447
  /** A cached viem public client for a chain. Pass `rpcUrl` to use your own RPC. */
448
448
  declare function getPublic(chain: ChainName, rpcUrl?: string): PublicClient;
449
+ /**
450
+ * Switch the connected wallet to a chain before signing/sending on it. An external wallet
451
+ * (MetaMask etc.) rejects a tx/typed-data whose chain differs from its active chain, so
452
+ * every trade fn calls this first. Idempotent (no prompt if already there); adds the chain
453
+ * if unknown (4902). Skipped for local signers, which sign any chain.
454
+ */
455
+ declare function ensureWalletChain(walletClient: WalletClient, chain: ChainName): Promise<void>;
449
456
  /**
450
457
  * Read the live grid: current epoch -> every series (seriesAt) -> strike + a quote.
451
458
  * Leverage = spot / premium (the ask from the quote API). Returns [] if the pool
@@ -524,4 +531,4 @@ interface PositionArgs {
524
531
  */
525
532
  declare function getPosition(args: PositionArgs): Promise<Position>;
526
533
 
527
- export { ARB_CHAIN_ID, BASE_CHAIN_ID, type BuyArgs, type BuyGaslessArgs, CHAINS, type ChainConfig, type ChainId, type ChainName, type CloseArgs, type CloseGaslessArgs, ERC20_ABI, type ExecuteCall, FACILITATOR_URL_ARB, FACILITATOR_URL_BASE, type GaslessPosition, type GaslessResult, type Greeks, type Market, NEXUS_BOOTSTRAP, NEXUS_FACTORY, type Position, type PositionArgs, type PositionGaslessArgs, QUOTE_API, SPLIT_POOL_ABI, SPLIT_VAULT_ABI, type Side, type SplitQuote, type TradeResult, type TradingAccount, type UserOp, buy, buyGasless, close, closeGasless, deriveSmartWallet, deriveTradingAccount, facilitatorUrlForChain, getMarkets, getPosition, getPositionGasless, getPublic, getQuote, poolAddress, quoteTuple };
534
+ export { ARB_CHAIN_ID, BASE_CHAIN_ID, type BuyArgs, type BuyGaslessArgs, CHAINS, type ChainConfig, type ChainId, type ChainName, type CloseArgs, type CloseGaslessArgs, ERC20_ABI, type ExecuteCall, FACILITATOR_URL_ARB, FACILITATOR_URL_BASE, type GaslessPosition, type GaslessResult, type Greeks, type Market, NEXUS_BOOTSTRAP, NEXUS_FACTORY, type Position, type PositionArgs, type PositionGaslessArgs, QUOTE_API, SPLIT_POOL_ABI, SPLIT_VAULT_ABI, type Side, type SplitQuote, type TradeResult, type TradingAccount, type UserOp, buy, buyGasless, close, closeGasless, deriveSmartWallet, deriveTradingAccount, ensureWalletChain, facilitatorUrlForChain, getMarkets, getPosition, getPositionGasless, getPublic, getQuote, poolAddress, quoteTuple };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 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 } from './chunk-MZEPXYHI.js';
1
+ 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, ensureWalletChain, facilitatorUrlForChain, getMarkets, getPosition, getPositionGasless, getPublic, getQuote, poolAddress, quoteTuple } from './chunk-FDWH3X5N.js';
package/dist/widget.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getMarkets, getPositionGasless, getPosition, deriveTradingAccount, buyGasless, buy, closeGasless, close } from './chunk-MZEPXYHI.js';
1
+ import { getMarkets, getPositionGasless, getPosition, deriveTradingAccount, buyGasless, buy, closeGasless, close } from './chunk-FDWH3X5N.js';
2
2
  import { useState, useId, useMemo, useEffect, useCallback } from 'react';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
4
 
@@ -36,7 +36,7 @@ function SplitTradeWidget({
36
36
  const [side, setSide] = useState("long");
37
37
  const [markets, setMarkets] = useState([]);
38
38
  const [picked, setPicked] = useState(null);
39
- const [amount, setAmount] = useState("0.1");
39
+ const [amount, setAmount] = useState("5");
40
40
  const [position, setPosition] = useState(null);
41
41
  const [status, setStatus] = useState("");
42
42
  const [busy, setBusy] = useState(false);
@@ -105,11 +105,17 @@ function SplitTradeWidget({
105
105
  setStatus("Connect a wallet first.");
106
106
  return;
107
107
  }
108
- const qN = Number(amount);
109
- if (!(qN > 0)) {
110
- setStatus("Enter an amount > 0.");
108
+ const usd = Number(amount);
109
+ if (!(usd > 0)) {
110
+ setStatus("Enter a dollar amount > 0.");
111
111
  return;
112
112
  }
113
+ const m = markets.find((x) => x.seriesId === picked);
114
+ if (!m || !(m.premium > 0)) {
115
+ setStatus("Market not ready \u2014 try again.");
116
+ return;
117
+ }
118
+ const qN = usd / m.premium;
113
119
  setBusy(true);
114
120
  try {
115
121
  if (gasless) {
@@ -117,11 +123,11 @@ function SplitTradeWidget({
117
123
  const t = trading ?? await deriveTradingAccount(walletClient, account, chain);
118
124
  if (!trading) setTrading(t);
119
125
  const r = await buyGasless({ chain, side, seriesId: picked, qN, walletClient, account, trading: t });
120
- setStatus(`Bought ${qN} contracts. tx ${r.txHash.slice(0, 10)}\u2026`);
126
+ setStatus(`Bought $${usd} of ${side}. tx ${r.txHash.slice(0, 10)}\u2026`);
121
127
  } else {
122
128
  setStatus("Confirm in your wallet (approve, then buy)\u2026");
123
129
  const r = await buy({ chain, side, seriesId: picked, qN, walletClient, account });
124
- setStatus(`Bought ${r.qN} contracts. tx ${r.txHash.slice(0, 10)}\u2026`);
130
+ setStatus(`Bought $${usd} of ${side}. tx ${r.txHash.slice(0, 10)}\u2026`);
125
131
  }
126
132
  await refreshPosition();
127
133
  } catch (e) {
@@ -181,31 +187,30 @@ function SplitTradeWidget({
181
187
  children: [
182
188
  /* @__PURE__ */ jsx("div", { className: "swt-lev", children: m.leverage > 0 ? `${m.leverage.toFixed(0)}x` : "\u2014" }),
183
189
  /* @__PURE__ */ jsxs("div", { className: "swt-muted", children: [
184
- "$",
185
- m.strike.toLocaleString()
190
+ "ETH ",
191
+ side === "long" ? ">" : "<",
192
+ " $",
193
+ (side === "long" ? m.strike + m.premium : m.strike - m.premium).toLocaleString(void 0, { maximumFractionDigits: 0 })
186
194
  ] }),
187
- /* @__PURE__ */ jsxs("div", { className: "swt-muted-sm", children: [
188
- "$",
189
- m.premium.toFixed(2),
190
- "/contract"
191
- ] })
195
+ /* @__PURE__ */ jsx("div", { className: "swt-muted-sm", children: "by expiry" })
192
196
  ]
193
197
  },
194
198
  m.seriesId
195
199
  ))
196
200
  ] }),
197
201
  /* @__PURE__ */ jsxs("div", { className: "swt-row", children: [
202
+ /* @__PURE__ */ jsx("span", { className: "swt-muted", children: "$" }),
198
203
  /* @__PURE__ */ jsx(
199
204
  "input",
200
205
  {
201
206
  value: amount,
202
207
  onChange: (e) => setAmount(e.target.value),
203
208
  inputMode: "decimal",
204
- placeholder: "0.1",
209
+ placeholder: "5",
205
210
  className: "swt-input"
206
211
  }
207
212
  ),
208
- /* @__PURE__ */ jsx("span", { className: "swt-muted", children: "contracts" }),
213
+ /* @__PURE__ */ jsx("span", { className: "swt-muted", children: "USDC" }),
209
214
  /* @__PURE__ */ jsx(
210
215
  "button",
211
216
  {
package/package.json CHANGED
@@ -1,16 +1,26 @@
1
1
  {
2
2
  "name": "@splitmarkets/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Oracle-free, non-liquidatable ETH options — viem client + optional React widget for Split (split.markets). Integrate leverage your users can't get liquidated out of.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
- "files": ["dist", "README.md", "SKILL.md"],
7
+ "files": [
8
+ "dist",
9
+ "README.md",
10
+ "SKILL.md"
11
+ ],
8
12
  "main": "./dist/index.js",
9
13
  "module": "./dist/index.js",
10
14
  "types": "./dist/index.d.ts",
11
15
  "exports": {
12
- ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" },
13
- "./widget": { "types": "./dist/widget.d.ts", "import": "./dist/widget.js" }
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js"
19
+ },
20
+ "./widget": {
21
+ "types": "./dist/widget.d.ts",
22
+ "import": "./dist/widget.js"
23
+ }
14
24
  },
15
25
  "scripts": {
16
26
  "build": "tsup",
@@ -18,14 +28,25 @@
18
28
  "test:integration": "vitest run test/integration.test.ts",
19
29
  "prepublishOnly": "npm run build && npm test"
20
30
  },
21
- "keywords": ["ethereum", "base", "arbitrum", "options", "defi", "viem", "split", "no-liquidation"],
31
+ "keywords": [
32
+ "ethereum",
33
+ "base",
34
+ "arbitrum",
35
+ "options",
36
+ "defi",
37
+ "viem",
38
+ "split",
39
+ "no-liquidation"
40
+ ],
22
41
  "homepage": "https://split.markets",
23
42
  "peerDependencies": {
24
43
  "viem": "^2",
25
44
  "react": ">=18"
26
45
  },
27
46
  "peerDependenciesMeta": {
28
- "react": { "optional": true }
47
+ "react": {
48
+ "optional": true
49
+ }
29
50
  },
30
51
  "devDependencies": {
31
52
  "@types/react": "^19",