@node-dlc/core
Version:
140 lines (116 loc) • 4.46 kB
text/typescript
import {
ContractDescriptorV1,
ContractInfo,
ContractInfoV0,
DigitDecompositionEventDescriptorV0,
HyperbolaPayoutCurvePiece,
MessageType,
PayoutFunctionV0,
} from '@node-dlc/messaging';
import BN from 'bignumber.js';
import { HyperbolaPayoutCurve } from '../HyperbolaPayoutCurve';
import { CoveredCall } from './CoveredCall';
import { ShortPut } from './ShortPut';
export interface OptionInfo {
contractSize: bigint;
strikePrice: bigint;
premium?: bigint;
type?: OptionType;
expiry: Date;
}
export type HasOfferCollateralSatoshis = {
offerCollateralSatoshis: bigint;
};
export type HasContractInfo = {
contractInfo: ContractInfo;
};
export type HasType = {
type: MessageType;
};
export type OptionType = 'put' | 'call';
export function getOptionInfoFromContractInfo(
_contractInfo: ContractInfo,
): OptionInfo {
if (_contractInfo.type !== MessageType.ContractInfoV0)
throw Error('Only ContractInfoV0 currently supported');
const contractInfo = _contractInfo as ContractInfoV0;
if (contractInfo.contractDescriptor.type !== MessageType.ContractDescriptorV1)
throw Error('Only ContractDescriptorV1 currently supported');
const oracleInfo = contractInfo.oracleInfo;
const { eventMaturityEpoch } = oracleInfo.announcement.oracleEvent;
const eventDescriptor = oracleInfo.announcement.oracleEvent
.eventDescriptor as DigitDecompositionEventDescriptorV0;
const { base: oracleBase, nbDigits: oracleDigits } = eventDescriptor;
if (
oracleInfo.announcement.oracleEvent.eventDescriptor.type !==
MessageType.DigitDecompositionEventDescriptorV0
)
throw Error('Only DigitDecompositionEventDescriptorV0 currently supported');
const contractDescriptor = contractInfo.contractDescriptor as ContractDescriptorV1;
if (contractDescriptor.payoutFunction.type !== MessageType.PayoutFunctionV0)
throw Error('Only PayoutFunctionV0 currently supported');
const payoutFunction = contractDescriptor.payoutFunction as PayoutFunctionV0;
if (payoutFunction.pieces.length === 0)
throw Error('PayoutFunction must have at least once PayoutCurvePiece');
if (payoutFunction.pieces.length > 1)
throw Error('More than one PayoutCurvePiece not supported');
const payoutCurvePiece = payoutFunction.pieces[0]
.payoutCurvePiece as HyperbolaPayoutCurvePiece;
if (
payoutCurvePiece.type !== MessageType.HyperbolaPayoutCurvePiece &&
payoutCurvePiece.type !== MessageType.OldHyperbolaPayoutCurvePiece
)
throw Error('Must be HyperbolaPayoutCurvePiece');
if (payoutCurvePiece.b !== BigInt(0) || payoutCurvePiece.c !== BigInt(0))
throw Error('b and c HyperbolaPayoutCurvePiece values must be 0');
const curve = HyperbolaPayoutCurve.fromPayoutCurvePiece(payoutCurvePiece);
const maxOutcome = BigInt(
new BN(oracleBase).pow(oracleDigits).minus(1).toString(10),
);
const isAscending = curve
.getPayout(maxOutcome)
.gt(Number(payoutFunction.endpointPayout0));
const expiry = new Date(eventMaturityEpoch * 1000);
const totalCollateral = contractInfo.totalCollateral;
// if curve is ascending, assume it is a put.
const contractSize = isAscending
? payoutCurvePiece.translatePayout - totalCollateral
: totalCollateral + payoutCurvePiece.translatePayout;
const strikePrice = payoutCurvePiece.d / contractSize;
// rebuild payout curve from option info and perform a sanity check
const { payoutCurve: sanityCurve } = isAscending
? ShortPut.buildCurve(
strikePrice,
contractSize,
totalCollateral,
oracleBase,
oracleDigits,
)
: CoveredCall.buildCurve(
strikePrice,
contractSize,
oracleBase,
oracleDigits,
);
const type = isAscending ? 'put' : 'call';
if (!curve.equals(sanityCurve))
throw new Error(
'Payout curve built from extracted OptionInfo does not match original payout curve',
);
return { contractSize, strikePrice, expiry, type };
}
export function getOptionInfoFromOffer(
offer: HasOfferCollateralSatoshis & HasContractInfo & HasType,
): OptionInfo {
if (
offer.type !== MessageType.DlcOfferV0 &&
offer.type !== MessageType.OrderOfferV0
)
throw Error('Only DlcOfferV0 and OrderOfferV0 currently supported');
const premium =
offer.contractInfo.totalCollateral - offer.offerCollateralSatoshis;
return {
...getOptionInfoFromContractInfo(offer.contractInfo),
premium,
};
}