@acta-markets/ts-sdk 0.0.12-beta → 0.0.13-beta

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.
@@ -21,3 +21,4 @@ __exportStar(require("./flows"), exports);
21
21
  __exportStar(require("./sponsoredTx"), exports);
22
22
  __exportStar(require("./apy"), exports);
23
23
  __exportStar(require("./discovery"), exports);
24
+ __exportStar(require("./sizing"), exports);
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * CSP quantity / size-rule conversion helpers.
4
+ *
5
+ * On the wire, `quantity` is always in underlying atomic units (e.g. SOL lamports).
6
+ * For covered calls the user thinks in underlying (SOL), so no conversion is needed.
7
+ * For cash-secured puts the user thinks in quote (USDC), so the frontend must convert
8
+ * between the user's USDC input and the wire's underlying quantity.
9
+ *
10
+ * These helpers handle that conversion and let the frontend display size_rule
11
+ * constraints in quote terms.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.quoteAmountToQuantity = quoteAmountToQuantity;
15
+ exports.quantityToQuoteAmount = quantityToQuoteAmount;
16
+ exports.sizeRuleInQuoteTerms = sizeRuleInQuoteTerms;
17
+ const apy_1 = require("./apy");
18
+ /**
19
+ * Convert a user's quote-denominated deposit (e.g. 450 USDC) to the wire
20
+ * quantity in underlying atomic units.
21
+ *
22
+ * Formula: `round(quoteAmount * 10^underlyingDecimals * PRICE_SCALE / strike1e9)`
23
+ *
24
+ * @param quoteAmount Human-readable quote amount (e.g. 450 for "450 USDC")
25
+ * @param strike1e9 Strike price, 1e9-scaled (e.g. 90_000_000_000 for $90)
26
+ * @param underlyingDecimals Decimals of the underlying mint (e.g. 9 for SOL)
27
+ * @returns Wire quantity (underlying atomic units)
28
+ */
29
+ function quoteAmountToQuantity(quoteAmount, strike1e9, underlyingDecimals) {
30
+ if (!Number.isFinite(quoteAmount) || quoteAmount < 0) {
31
+ throw new Error("quoteAmount must be a non-negative finite number");
32
+ }
33
+ if (!Number.isFinite(strike1e9) || strike1e9 <= 0) {
34
+ throw new Error("strike1e9 must be a positive finite number");
35
+ }
36
+ const atomicFactor = Math.pow(10, underlyingDecimals);
37
+ return Math.round(quoteAmount * atomicFactor * apy_1.PRICE_SCALE / strike1e9);
38
+ }
39
+ /**
40
+ * Convert a wire quantity (underlying atomic units) to a human-readable
41
+ * quote-denominated amount.
42
+ *
43
+ * Formula: `quantity * strike1e9 / PRICE_SCALE / 10^underlyingDecimals`
44
+ *
45
+ * @param quantity Wire quantity (underlying atomic units)
46
+ * @param strike1e9 Strike price, 1e9-scaled
47
+ * @param underlyingDecimals Decimals of the underlying mint
48
+ * @returns Human-readable quote amount (e.g. 450 for "450 USDC")
49
+ */
50
+ function quantityToQuoteAmount(quantity, strike1e9, underlyingDecimals) {
51
+ if (!Number.isFinite(quantity) || quantity < 0) {
52
+ throw new Error("quantity must be a non-negative finite number");
53
+ }
54
+ if (!Number.isFinite(strike1e9) || strike1e9 <= 0) {
55
+ throw new Error("strike1e9 must be a positive finite number");
56
+ }
57
+ const atomicFactor = Math.pow(10, underlyingDecimals);
58
+ return quantity * strike1e9 / apy_1.PRICE_SCALE / atomicFactor;
59
+ }
60
+ /**
61
+ * Convert a size_rule (underlying atomic units) to human-readable quote terms.
62
+ *
63
+ * Example: SOL/USDC CSP, strike $90, rule {min: 1e9, max: 10e9, step: 1e8}
64
+ * → returns {min: 90, max: 900, step: 9} (USDC)
65
+ *
66
+ * @param rule Size rule from MarketDescriptorInfo
67
+ * @param strike1e9 Strike price, 1e9-scaled
68
+ * @param underlyingDecimals Decimals of the underlying mint
69
+ */
70
+ function sizeRuleInQuoteTerms(rule, strike1e9, underlyingDecimals) {
71
+ if (!Number.isFinite(strike1e9) || strike1e9 <= 0) {
72
+ throw new Error("strike1e9 must be a positive finite number");
73
+ }
74
+ return {
75
+ min: quantityToQuoteAmount(rule.min_size, strike1e9, underlyingDecimals),
76
+ max: quantityToQuoteAmount(rule.max_size, strike1e9, underlyingDecimals),
77
+ step: quantityToQuoteAmount(rule.step, strike1e9, underlyingDecimals),
78
+ };
79
+ }
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const sizing_1 = require("./sizing");
4
+ describe("ws sizing helpers", () => {
5
+ // SOL: 9 decimals, strike $90 = 90_000_000_000 (1e9-scaled)
6
+ const SOL_DECIMALS = 9;
7
+ const STRIKE_90 = 90_000_000_000;
8
+ describe("quoteAmountToQuantity", () => {
9
+ it("converts 450 USDC at $90 strike to 5 SOL in lamports", () => {
10
+ expect((0, sizing_1.quoteAmountToQuantity)(450, STRIKE_90, SOL_DECIMALS)).toBe(5_000_000_000);
11
+ });
12
+ it("converts 90 USDC (min) to 1 SOL in lamports", () => {
13
+ expect((0, sizing_1.quoteAmountToQuantity)(90, STRIKE_90, SOL_DECIMALS)).toBe(1_000_000_000);
14
+ });
15
+ it("converts 9 USDC (one step) to 0.1 SOL in lamports", () => {
16
+ expect((0, sizing_1.quoteAmountToQuantity)(9, STRIKE_90, SOL_DECIMALS)).toBe(100_000_000);
17
+ });
18
+ it("returns 0 for 0 input", () => {
19
+ expect((0, sizing_1.quoteAmountToQuantity)(0, STRIKE_90, SOL_DECIMALS)).toBe(0);
20
+ });
21
+ it("throws on negative quoteAmount", () => {
22
+ expect(() => (0, sizing_1.quoteAmountToQuantity)(-1, STRIKE_90, SOL_DECIMALS)).toThrow("quoteAmount");
23
+ });
24
+ it("throws on zero strike", () => {
25
+ expect(() => (0, sizing_1.quoteAmountToQuantity)(100, 0, SOL_DECIMALS)).toThrow("strike1e9");
26
+ });
27
+ it("throws on negative strike", () => {
28
+ expect(() => (0, sizing_1.quoteAmountToQuantity)(100, -1, SOL_DECIMALS)).toThrow("strike1e9");
29
+ });
30
+ });
31
+ describe("quantityToQuoteAmount", () => {
32
+ it("converts 5 SOL lamports at $90 strike to 450 USDC", () => {
33
+ expect((0, sizing_1.quantityToQuoteAmount)(5_000_000_000, STRIKE_90, SOL_DECIMALS)).toBeCloseTo(450, 10);
34
+ });
35
+ it("converts 1 SOL lamports to 90 USDC", () => {
36
+ expect((0, sizing_1.quantityToQuoteAmount)(1_000_000_000, STRIKE_90, SOL_DECIMALS)).toBeCloseTo(90, 10);
37
+ });
38
+ it("converts 0.1 SOL lamports to 9 USDC", () => {
39
+ expect((0, sizing_1.quantityToQuoteAmount)(100_000_000, STRIKE_90, SOL_DECIMALS)).toBeCloseTo(9, 10);
40
+ });
41
+ it("returns 0 for 0 quantity", () => {
42
+ expect((0, sizing_1.quantityToQuoteAmount)(0, STRIKE_90, SOL_DECIMALS)).toBe(0);
43
+ });
44
+ it("throws on negative quantity", () => {
45
+ expect(() => (0, sizing_1.quantityToQuoteAmount)(-1, STRIKE_90, SOL_DECIMALS)).toThrow("quantity");
46
+ });
47
+ });
48
+ describe("round-trip", () => {
49
+ it("quoteAmount -> quantity -> quoteAmount is stable", () => {
50
+ const original = 450;
51
+ const quantity = (0, sizing_1.quoteAmountToQuantity)(original, STRIKE_90, SOL_DECIMALS);
52
+ const restored = (0, sizing_1.quantityToQuoteAmount)(quantity, STRIKE_90, SOL_DECIMALS);
53
+ expect(restored).toBeCloseTo(original, 6);
54
+ });
55
+ it("round-trips at step granularity (9 USDC)", () => {
56
+ const original = 9;
57
+ const quantity = (0, sizing_1.quoteAmountToQuantity)(original, STRIKE_90, SOL_DECIMALS);
58
+ const restored = (0, sizing_1.quantityToQuoteAmount)(quantity, STRIKE_90, SOL_DECIMALS);
59
+ expect(restored).toBeCloseTo(original, 6);
60
+ });
61
+ it("round-trips with different strike ($150)", () => {
62
+ const strike150 = 150_000_000_000;
63
+ const original = 300;
64
+ const quantity = (0, sizing_1.quoteAmountToQuantity)(original, strike150, SOL_DECIMALS);
65
+ expect(quantity).toBe(2_000_000_000); // 2 SOL
66
+ const restored = (0, sizing_1.quantityToQuoteAmount)(quantity, strike150, SOL_DECIMALS);
67
+ expect(restored).toBeCloseTo(original, 6);
68
+ });
69
+ });
70
+ describe("sizeRuleInQuoteTerms", () => {
71
+ const rule = { min_size: 1_000_000_000, max_size: 10_000_000_000, step: 100_000_000 };
72
+ it("converts SOL size_rule to USDC terms at $90 strike", () => {
73
+ const result = (0, sizing_1.sizeRuleInQuoteTerms)(rule, STRIKE_90, SOL_DECIMALS);
74
+ expect(result.min).toBeCloseTo(90, 6);
75
+ expect(result.max).toBeCloseTo(900, 6);
76
+ expect(result.step).toBeCloseTo(9, 6);
77
+ });
78
+ it("converts at $150 strike", () => {
79
+ const strike150 = 150_000_000_000;
80
+ const result = (0, sizing_1.sizeRuleInQuoteTerms)(rule, strike150, SOL_DECIMALS);
81
+ expect(result.min).toBeCloseTo(150, 6);
82
+ expect(result.max).toBeCloseTo(1500, 6);
83
+ expect(result.step).toBeCloseTo(15, 6);
84
+ });
85
+ it("throws on zero strike", () => {
86
+ expect(() => (0, sizing_1.sizeRuleInQuoteTerms)(rule, 0, SOL_DECIMALS)).toThrow("strike1e9");
87
+ });
88
+ });
89
+ describe("non-9-decimal underlying", () => {
90
+ // ETH: 8 decimals
91
+ const ETH_DECIMALS = 8;
92
+ const STRIKE_3000 = 3_000_000_000_000; // $3000
93
+ it("converts 6000 USDC at $3000 strike to 2 ETH (8 decimals)", () => {
94
+ expect((0, sizing_1.quoteAmountToQuantity)(6000, STRIKE_3000, ETH_DECIMALS)).toBe(200_000_000);
95
+ });
96
+ it("converts 2 ETH back to 6000 USDC", () => {
97
+ expect((0, sizing_1.quantityToQuoteAmount)(200_000_000, STRIKE_3000, ETH_DECIMALS)).toBeCloseTo(6000, 6);
98
+ });
99
+ });
100
+ });
@@ -5,3 +5,4 @@ export * from "./flows";
5
5
  export * from "./sponsoredTx";
6
6
  export * from "./apy";
7
7
  export * from "./discovery";
8
+ export * from "./sizing";
package/dist/ws/index.js CHANGED
@@ -5,3 +5,4 @@ export * from "./flows";
5
5
  export * from "./sponsoredTx";
6
6
  export * from "./apy";
7
7
  export * from "./discovery";
8
+ export * from "./sizing";
@@ -0,0 +1,52 @@
1
+ /**
2
+ * CSP quantity / size-rule conversion helpers.
3
+ *
4
+ * On the wire, `quantity` is always in underlying atomic units (e.g. SOL lamports).
5
+ * For covered calls the user thinks in underlying (SOL), so no conversion is needed.
6
+ * For cash-secured puts the user thinks in quote (USDC), so the frontend must convert
7
+ * between the user's USDC input and the wire's underlying quantity.
8
+ *
9
+ * These helpers handle that conversion and let the frontend display size_rule
10
+ * constraints in quote terms.
11
+ */
12
+ import type { QuantitySizeRuleLike } from "./wirePolicy";
13
+ /**
14
+ * Convert a user's quote-denominated deposit (e.g. 450 USDC) to the wire
15
+ * quantity in underlying atomic units.
16
+ *
17
+ * Formula: `round(quoteAmount * 10^underlyingDecimals * PRICE_SCALE / strike1e9)`
18
+ *
19
+ * @param quoteAmount Human-readable quote amount (e.g. 450 for "450 USDC")
20
+ * @param strike1e9 Strike price, 1e9-scaled (e.g. 90_000_000_000 for $90)
21
+ * @param underlyingDecimals Decimals of the underlying mint (e.g. 9 for SOL)
22
+ * @returns Wire quantity (underlying atomic units)
23
+ */
24
+ export declare function quoteAmountToQuantity(quoteAmount: number, strike1e9: number, underlyingDecimals: number): number;
25
+ /**
26
+ * Convert a wire quantity (underlying atomic units) to a human-readable
27
+ * quote-denominated amount.
28
+ *
29
+ * Formula: `quantity * strike1e9 / PRICE_SCALE / 10^underlyingDecimals`
30
+ *
31
+ * @param quantity Wire quantity (underlying atomic units)
32
+ * @param strike1e9 Strike price, 1e9-scaled
33
+ * @param underlyingDecimals Decimals of the underlying mint
34
+ * @returns Human-readable quote amount (e.g. 450 for "450 USDC")
35
+ */
36
+ export declare function quantityToQuoteAmount(quantity: number, strike1e9: number, underlyingDecimals: number): number;
37
+ export type QuoteDenominatedSizeRule = {
38
+ min: number;
39
+ max: number;
40
+ step: number;
41
+ };
42
+ /**
43
+ * Convert a size_rule (underlying atomic units) to human-readable quote terms.
44
+ *
45
+ * Example: SOL/USDC CSP, strike $90, rule {min: 1e9, max: 10e9, step: 1e8}
46
+ * → returns {min: 90, max: 900, step: 9} (USDC)
47
+ *
48
+ * @param rule Size rule from MarketDescriptorInfo
49
+ * @param strike1e9 Strike price, 1e9-scaled
50
+ * @param underlyingDecimals Decimals of the underlying mint
51
+ */
52
+ export declare function sizeRuleInQuoteTerms(rule: QuantitySizeRuleLike, strike1e9: number, underlyingDecimals: number): QuoteDenominatedSizeRule;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * CSP quantity / size-rule conversion helpers.
3
+ *
4
+ * On the wire, `quantity` is always in underlying atomic units (e.g. SOL lamports).
5
+ * For covered calls the user thinks in underlying (SOL), so no conversion is needed.
6
+ * For cash-secured puts the user thinks in quote (USDC), so the frontend must convert
7
+ * between the user's USDC input and the wire's underlying quantity.
8
+ *
9
+ * These helpers handle that conversion and let the frontend display size_rule
10
+ * constraints in quote terms.
11
+ */
12
+ import { PRICE_SCALE } from "./apy";
13
+ /**
14
+ * Convert a user's quote-denominated deposit (e.g. 450 USDC) to the wire
15
+ * quantity in underlying atomic units.
16
+ *
17
+ * Formula: `round(quoteAmount * 10^underlyingDecimals * PRICE_SCALE / strike1e9)`
18
+ *
19
+ * @param quoteAmount Human-readable quote amount (e.g. 450 for "450 USDC")
20
+ * @param strike1e9 Strike price, 1e9-scaled (e.g. 90_000_000_000 for $90)
21
+ * @param underlyingDecimals Decimals of the underlying mint (e.g. 9 for SOL)
22
+ * @returns Wire quantity (underlying atomic units)
23
+ */
24
+ export function quoteAmountToQuantity(quoteAmount, strike1e9, underlyingDecimals) {
25
+ if (!Number.isFinite(quoteAmount) || quoteAmount < 0) {
26
+ throw new Error("quoteAmount must be a non-negative finite number");
27
+ }
28
+ if (!Number.isFinite(strike1e9) || strike1e9 <= 0) {
29
+ throw new Error("strike1e9 must be a positive finite number");
30
+ }
31
+ const atomicFactor = Math.pow(10, underlyingDecimals);
32
+ return Math.round(quoteAmount * atomicFactor * PRICE_SCALE / strike1e9);
33
+ }
34
+ /**
35
+ * Convert a wire quantity (underlying atomic units) to a human-readable
36
+ * quote-denominated amount.
37
+ *
38
+ * Formula: `quantity * strike1e9 / PRICE_SCALE / 10^underlyingDecimals`
39
+ *
40
+ * @param quantity Wire quantity (underlying atomic units)
41
+ * @param strike1e9 Strike price, 1e9-scaled
42
+ * @param underlyingDecimals Decimals of the underlying mint
43
+ * @returns Human-readable quote amount (e.g. 450 for "450 USDC")
44
+ */
45
+ export function quantityToQuoteAmount(quantity, strike1e9, underlyingDecimals) {
46
+ if (!Number.isFinite(quantity) || quantity < 0) {
47
+ throw new Error("quantity must be a non-negative finite number");
48
+ }
49
+ if (!Number.isFinite(strike1e9) || strike1e9 <= 0) {
50
+ throw new Error("strike1e9 must be a positive finite number");
51
+ }
52
+ const atomicFactor = Math.pow(10, underlyingDecimals);
53
+ return quantity * strike1e9 / PRICE_SCALE / atomicFactor;
54
+ }
55
+ /**
56
+ * Convert a size_rule (underlying atomic units) to human-readable quote terms.
57
+ *
58
+ * Example: SOL/USDC CSP, strike $90, rule {min: 1e9, max: 10e9, step: 1e8}
59
+ * → returns {min: 90, max: 900, step: 9} (USDC)
60
+ *
61
+ * @param rule Size rule from MarketDescriptorInfo
62
+ * @param strike1e9 Strike price, 1e9-scaled
63
+ * @param underlyingDecimals Decimals of the underlying mint
64
+ */
65
+ export function sizeRuleInQuoteTerms(rule, strike1e9, underlyingDecimals) {
66
+ if (!Number.isFinite(strike1e9) || strike1e9 <= 0) {
67
+ throw new Error("strike1e9 must be a positive finite number");
68
+ }
69
+ return {
70
+ min: quantityToQuoteAmount(rule.min_size, strike1e9, underlyingDecimals),
71
+ max: quantityToQuoteAmount(rule.max_size, strike1e9, underlyingDecimals),
72
+ step: quantityToQuoteAmount(rule.step, strike1e9, underlyingDecimals),
73
+ };
74
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,98 @@
1
+ import { quoteAmountToQuantity, quantityToQuoteAmount, sizeRuleInQuoteTerms, } from "./sizing";
2
+ describe("ws sizing helpers", () => {
3
+ // SOL: 9 decimals, strike $90 = 90_000_000_000 (1e9-scaled)
4
+ const SOL_DECIMALS = 9;
5
+ const STRIKE_90 = 90_000_000_000;
6
+ describe("quoteAmountToQuantity", () => {
7
+ it("converts 450 USDC at $90 strike to 5 SOL in lamports", () => {
8
+ expect(quoteAmountToQuantity(450, STRIKE_90, SOL_DECIMALS)).toBe(5_000_000_000);
9
+ });
10
+ it("converts 90 USDC (min) to 1 SOL in lamports", () => {
11
+ expect(quoteAmountToQuantity(90, STRIKE_90, SOL_DECIMALS)).toBe(1_000_000_000);
12
+ });
13
+ it("converts 9 USDC (one step) to 0.1 SOL in lamports", () => {
14
+ expect(quoteAmountToQuantity(9, STRIKE_90, SOL_DECIMALS)).toBe(100_000_000);
15
+ });
16
+ it("returns 0 for 0 input", () => {
17
+ expect(quoteAmountToQuantity(0, STRIKE_90, SOL_DECIMALS)).toBe(0);
18
+ });
19
+ it("throws on negative quoteAmount", () => {
20
+ expect(() => quoteAmountToQuantity(-1, STRIKE_90, SOL_DECIMALS)).toThrow("quoteAmount");
21
+ });
22
+ it("throws on zero strike", () => {
23
+ expect(() => quoteAmountToQuantity(100, 0, SOL_DECIMALS)).toThrow("strike1e9");
24
+ });
25
+ it("throws on negative strike", () => {
26
+ expect(() => quoteAmountToQuantity(100, -1, SOL_DECIMALS)).toThrow("strike1e9");
27
+ });
28
+ });
29
+ describe("quantityToQuoteAmount", () => {
30
+ it("converts 5 SOL lamports at $90 strike to 450 USDC", () => {
31
+ expect(quantityToQuoteAmount(5_000_000_000, STRIKE_90, SOL_DECIMALS)).toBeCloseTo(450, 10);
32
+ });
33
+ it("converts 1 SOL lamports to 90 USDC", () => {
34
+ expect(quantityToQuoteAmount(1_000_000_000, STRIKE_90, SOL_DECIMALS)).toBeCloseTo(90, 10);
35
+ });
36
+ it("converts 0.1 SOL lamports to 9 USDC", () => {
37
+ expect(quantityToQuoteAmount(100_000_000, STRIKE_90, SOL_DECIMALS)).toBeCloseTo(9, 10);
38
+ });
39
+ it("returns 0 for 0 quantity", () => {
40
+ expect(quantityToQuoteAmount(0, STRIKE_90, SOL_DECIMALS)).toBe(0);
41
+ });
42
+ it("throws on negative quantity", () => {
43
+ expect(() => quantityToQuoteAmount(-1, STRIKE_90, SOL_DECIMALS)).toThrow("quantity");
44
+ });
45
+ });
46
+ describe("round-trip", () => {
47
+ it("quoteAmount -> quantity -> quoteAmount is stable", () => {
48
+ const original = 450;
49
+ const quantity = quoteAmountToQuantity(original, STRIKE_90, SOL_DECIMALS);
50
+ const restored = quantityToQuoteAmount(quantity, STRIKE_90, SOL_DECIMALS);
51
+ expect(restored).toBeCloseTo(original, 6);
52
+ });
53
+ it("round-trips at step granularity (9 USDC)", () => {
54
+ const original = 9;
55
+ const quantity = quoteAmountToQuantity(original, STRIKE_90, SOL_DECIMALS);
56
+ const restored = quantityToQuoteAmount(quantity, STRIKE_90, SOL_DECIMALS);
57
+ expect(restored).toBeCloseTo(original, 6);
58
+ });
59
+ it("round-trips with different strike ($150)", () => {
60
+ const strike150 = 150_000_000_000;
61
+ const original = 300;
62
+ const quantity = quoteAmountToQuantity(original, strike150, SOL_DECIMALS);
63
+ expect(quantity).toBe(2_000_000_000); // 2 SOL
64
+ const restored = quantityToQuoteAmount(quantity, strike150, SOL_DECIMALS);
65
+ expect(restored).toBeCloseTo(original, 6);
66
+ });
67
+ });
68
+ describe("sizeRuleInQuoteTerms", () => {
69
+ const rule = { min_size: 1_000_000_000, max_size: 10_000_000_000, step: 100_000_000 };
70
+ it("converts SOL size_rule to USDC terms at $90 strike", () => {
71
+ const result = sizeRuleInQuoteTerms(rule, STRIKE_90, SOL_DECIMALS);
72
+ expect(result.min).toBeCloseTo(90, 6);
73
+ expect(result.max).toBeCloseTo(900, 6);
74
+ expect(result.step).toBeCloseTo(9, 6);
75
+ });
76
+ it("converts at $150 strike", () => {
77
+ const strike150 = 150_000_000_000;
78
+ const result = sizeRuleInQuoteTerms(rule, strike150, SOL_DECIMALS);
79
+ expect(result.min).toBeCloseTo(150, 6);
80
+ expect(result.max).toBeCloseTo(1500, 6);
81
+ expect(result.step).toBeCloseTo(15, 6);
82
+ });
83
+ it("throws on zero strike", () => {
84
+ expect(() => sizeRuleInQuoteTerms(rule, 0, SOL_DECIMALS)).toThrow("strike1e9");
85
+ });
86
+ });
87
+ describe("non-9-decimal underlying", () => {
88
+ // ETH: 8 decimals
89
+ const ETH_DECIMALS = 8;
90
+ const STRIKE_3000 = 3_000_000_000_000; // $3000
91
+ it("converts 6000 USDC at $3000 strike to 2 ETH (8 decimals)", () => {
92
+ expect(quoteAmountToQuantity(6000, STRIKE_3000, ETH_DECIMALS)).toBe(200_000_000);
93
+ });
94
+ it("converts 2 ETH back to 6000 USDC", () => {
95
+ expect(quantityToQuoteAmount(200_000_000, STRIKE_3000, ETH_DECIMALS)).toBeCloseTo(6000, 6);
96
+ });
97
+ });
98
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acta-markets/ts-sdk",
3
- "version": "0.0.12-beta",
3
+ "version": "0.0.13-beta",
4
4
  "description": "TypeScript SDK for Acta Protocol",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",