@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.
- package/dist/cjs/ws/index.js +1 -0
- package/dist/cjs/ws/sizing.js +79 -0
- package/dist/cjs/ws/sizing.test.js +100 -0
- package/dist/ws/index.d.ts +1 -0
- package/dist/ws/index.js +1 -0
- package/dist/ws/sizing.d.ts +52 -0
- package/dist/ws/sizing.js +74 -0
- package/dist/ws/sizing.test.d.ts +1 -0
- package/dist/ws/sizing.test.js +98 -0
- package/package.json +1 -1
package/dist/cjs/ws/index.js
CHANGED
|
@@ -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
|
+
});
|
package/dist/ws/index.d.ts
CHANGED
package/dist/ws/index.js
CHANGED
|
@@ -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
|
+
});
|