UNPKG

@covenance/dlc

Version:

Crypto and Bitcoin functions for Covenance DLC implementation

141 lines (121 loc) 4.85 kB
import { expect } from 'chai'; import { Transaction, Address, PrivateKey, Script, Networks, PublicKey } from '../../src/btc'; import { createLiquidationCets, createDlcInitTx, selectLiquidationOutcomes } from '../../src/cet/transactions'; import { OracleEvent, LoanConfig } from '../../src/cet/types'; import { Point, utils } from '../../src/crypto/secp256k1'; import { EventOutcomeHash, PrivKey, PubKey } from '../../src/crypto/types'; import { sha256 } from '../../src'; describe('createLiquidationCets', () => { let borrowerKey: PrivateKey; let lenderKey: PrivateKey; let borrowerPubKey: PublicKey; let borrowerAddress: Address; let lenderAddress: Address; let borrowerDlcPrivateKey: PrivKey; let borrowerDlcPubKey: PubKey; let lenderDlcPrivateKey: PrivKey; let lenderDlcPubKey: PubKey; let dlcUtxo: any; let config: LoanConfig; before(() => { // Create test keys and addresses borrowerKey = new PrivateKey(); lenderKey = new PrivateKey(); borrowerPubKey = borrowerKey.toPublicKey(); borrowerAddress = new Address(borrowerPubKey, Networks.testnet, 'witnesspubkeyhash'); lenderAddress = new Address(lenderKey.toPublicKey(), Networks.testnet, 'witnesspubkeyhash'); borrowerDlcPrivateKey = utils.randomPrivateKey(); borrowerDlcPubKey = Point.fromPrivateKey(borrowerDlcPrivateKey); lenderDlcPrivateKey = utils.randomPrivateKey(); lenderDlcPubKey = Point.fromPrivateKey(lenderDlcPrivateKey); // Create DLC init transaction const borrowedAmountUsd = 50_000; const liquidationThreshold = 0.8; const annualInterestRate = 0.1; const collateralAmount = 0.8; // in BTC const inputAmount = collateralAmount * 2; const collateralUtxos = [{ txId: 'a'.repeat(64), outputIndex: 0, satoshis: inputAmount * 100000000, script: (Script as any).buildWitnessV0Out(borrowerAddress) }]; const dlcInitTx = createDlcInitTx( collateralUtxos, collateralAmount * 100000000, borrowerDlcPubKey, lenderDlcPubKey, borrowerAddress ); dlcUtxo = dlcInitTx.dlcUtxo; config = { collateralAmount, annualInterestRate, liquidationThreshold, borrowedAmount: borrowedAmountUsd, penaltyPercentage: 0.1 }; }); // Helper function to create a series of events with increasing prices const createEvents = async ( count: number, startTime: number = (Date.now() / 1000) | 0, btcPriceRange: readonly [number, number] = [50000, 150000], step = 500 ): Promise<OracleEvent[]> => { const [min, max] = btcPriceRange; const n = ((max - min) / step) | 0; const events = new Array<OracleEvent>(count); for (let i = 0; i < count; i++) { const sigs = new Array<Point>(n); for (let j = 0; j < n; j++) sigs[j] = Point.fromPrivateKey(utils.randomPrivateKey()); const outcomeHashes = new Array<EventOutcomeHash>(n); for (let j = 0; j < n; j++) { outcomeHashes[j] = await sha256(new Uint8Array([i, j])); } events[i] = { id: `event${i}`, timestamp: startTime + i * 60 * 10, outcomeSignaturePoints: sigs, outcomePrices: Array.from({ length: n }, (_, j) => min + j * step), outcomeHashes: outcomeHashes }; } return events; }; it('should create valid CETs for many events', async function() { this.timeout(10000); const events = await createEvents(100); const outcomes = selectLiquidationOutcomes(events, config); const oracleCets = createLiquidationCets( config, dlcUtxo, borrowerAddress, lenderAddress, outcomes ); // Verify transaction structure expect(oracleCets).to.have.lengthOf(100); // Verify each CET oracleCets.forEach((oracleCet) => { const cet = oracleCet.cetTx; expect(cet).to.be.instanceOf(Transaction); expect(cet.inputs.length).to.equal(1); expect(cet.outputs.length).to.equal(2); expect(cet.inputs[0].prevTxId.toString('hex')).to.equal(dlcUtxo.txId); expect(cet.inputs[0].outputIndex).to.equal(dlcUtxo.outputIndex); expect(cet.outputs[0].script.toHex()).to.include('0014'); expect(cet.outputs[1].script.toHex()).to.include('0014'); expect(oracleCet.lenderAmount).to.be.greaterThan(0); expect(oracleCet.borrowerAmount).to.be.greaterThan(0); expect(oracleCet.lenderAmount + oracleCet.borrowerAmount).to.equal(config.collateralAmount); }); }); it('should throw error when no suitable liquidation price is found', async () => { // Create event with all prices above liquidation price const events = await createEvents(1, 0, [120000, 130000]); // High BTC price range expect(() => { selectLiquidationOutcomes(events, config); }).to.throw(/^No grid price/); }); });