@argonprotocol/mainchain 1.3.18 → 1.3.20
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/browser/index.d.ts +93 -69
- package/browser/index.js +301 -289
- package/browser/index.js.map +1 -1
- package/lib/index.cjs +302 -290
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +93 -69
- package/lib/index.d.ts +93 -69
- package/lib/index.js +302 -290
- package/lib/index.js.map +1 -1
- package/package.json +3 -3
package/browser/index.js
CHANGED
|
@@ -561,8 +561,7 @@ var Vault = class _Vault {
|
|
|
561
561
|
baseFee,
|
|
562
562
|
bitcoinXpub,
|
|
563
563
|
tip,
|
|
564
|
-
doNotExceedBalance
|
|
565
|
-
txProgressCallback
|
|
564
|
+
doNotExceedBalance
|
|
566
565
|
} = args;
|
|
567
566
|
let xpubBytes = hexToU8a(bitcoinXpub);
|
|
568
567
|
if (xpubBytes.length !== 78) {
|
|
@@ -605,7 +604,6 @@ var Vault = class _Vault {
|
|
|
605
604
|
...args,
|
|
606
605
|
useLatestNonce: true
|
|
607
606
|
});
|
|
608
|
-
const tickDuration = config.tickDurationMillis ?? await client.query.ticks.genesisTicker().then((x) => x.tickDurationMillis.toNumber());
|
|
609
607
|
async function getVault() {
|
|
610
608
|
await result.waitForFinalizedBlock;
|
|
611
609
|
let vaultId;
|
|
@@ -618,11 +616,7 @@ var Vault = class _Vault {
|
|
|
618
616
|
if (vaultId === void 0) {
|
|
619
617
|
throw new Error("Vault creation failed, no VaultCreated event found");
|
|
620
618
|
}
|
|
621
|
-
|
|
622
|
-
if (rawVault.isNone) {
|
|
623
|
-
throw new Error("Vault creation failed, vault not found");
|
|
624
|
-
}
|
|
625
|
-
return new _Vault(vaultId, rawVault.unwrap(), tickDuration);
|
|
619
|
+
return _Vault.get(client, vaultId, config.tickDurationMillis);
|
|
626
620
|
}
|
|
627
621
|
return { getVault, txResult: result };
|
|
628
622
|
}
|
|
@@ -641,22 +635,277 @@ function fromFixedNumber(value, decimals = FIXED_U128_DECIMALS) {
|
|
|
641
635
|
var FIXED_U128_DECIMALS = 18;
|
|
642
636
|
var PERMILL_DECIMALS = 6;
|
|
643
637
|
var SATS_PER_BTC = 100000000n;
|
|
644
|
-
var
|
|
645
|
-
constructor(
|
|
646
|
-
this
|
|
638
|
+
var BitcoinLock = class _BitcoinLock {
|
|
639
|
+
constructor(data) {
|
|
640
|
+
__publicField(this, "utxoId");
|
|
641
|
+
__publicField(this, "p2wshScriptHashHex");
|
|
642
|
+
__publicField(this, "vaultId");
|
|
643
|
+
__publicField(this, "peggedPrice");
|
|
644
|
+
__publicField(this, "liquidityPromised");
|
|
645
|
+
__publicField(this, "ownerAccount");
|
|
646
|
+
__publicField(this, "satoshis");
|
|
647
|
+
__publicField(this, "vaultPubkey");
|
|
648
|
+
__publicField(this, "securityFees");
|
|
649
|
+
__publicField(this, "vaultClaimPubkey");
|
|
650
|
+
__publicField(this, "ownerPubkey");
|
|
651
|
+
__publicField(this, "vaultXpubSources");
|
|
652
|
+
__publicField(this, "vaultClaimHeight");
|
|
653
|
+
__publicField(this, "openClaimHeight");
|
|
654
|
+
__publicField(this, "createdAtHeight");
|
|
655
|
+
__publicField(this, "isVerified");
|
|
656
|
+
__publicField(this, "isRejectedNeedsRelease");
|
|
657
|
+
__publicField(this, "fundHoldExtensionsByBitcoinExpirationHeight");
|
|
658
|
+
this.utxoId = data.utxoId;
|
|
659
|
+
this.p2wshScriptHashHex = data.p2wshScriptHashHex;
|
|
660
|
+
this.vaultId = data.vaultId;
|
|
661
|
+
this.peggedPrice = data.peggedPrice;
|
|
662
|
+
this.liquidityPromised = data.liquidityPromised;
|
|
663
|
+
this.ownerAccount = data.ownerAccount;
|
|
664
|
+
this.satoshis = data.satoshis;
|
|
665
|
+
this.vaultPubkey = data.vaultPubkey;
|
|
666
|
+
this.securityFees = data.securityFees;
|
|
667
|
+
this.vaultClaimPubkey = data.vaultClaimPubkey;
|
|
668
|
+
this.ownerPubkey = data.ownerPubkey;
|
|
669
|
+
this.vaultXpubSources = data.vaultXpubSources;
|
|
670
|
+
this.vaultClaimHeight = data.vaultClaimHeight;
|
|
671
|
+
this.openClaimHeight = data.openClaimHeight;
|
|
672
|
+
this.createdAtHeight = data.createdAtHeight;
|
|
673
|
+
this.isVerified = data.isVerified;
|
|
674
|
+
this.isRejectedNeedsRelease = data.isRejectedNeedsRelease;
|
|
675
|
+
this.fundHoldExtensionsByBitcoinExpirationHeight = data.fundHoldExtensionsByBitcoinExpirationHeight;
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Gets the UTXO reference by ID.
|
|
679
|
+
* @param client - client at the block height to query the UTXO reference at a specific point in time.
|
|
680
|
+
* @return An object containing the transaction ID and output index, or undefined if not found.
|
|
681
|
+
* @return.txid - The Bitcoin transaction ID of the UTXO.
|
|
682
|
+
* @return.vout - The output index of the UTXO in the transaction.
|
|
683
|
+
* @return.bitcoinTxid - The Bitcoin transaction ID of the UTXO formatted in little endian
|
|
684
|
+
*/
|
|
685
|
+
async getUtxoRef(client) {
|
|
686
|
+
const refRaw = await client.query.bitcoinUtxos.utxoIdToRef(this.utxoId);
|
|
687
|
+
if (!refRaw) {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
const ref = refRaw.unwrap();
|
|
691
|
+
const txid = u8aToHex(ref.txid);
|
|
692
|
+
const bitcoinTxid = u8aToHex(ref.txid.reverse());
|
|
693
|
+
const vout = ref.outputIndex.toNumber();
|
|
694
|
+
return { txid, vout, bitcoinTxid };
|
|
695
|
+
}
|
|
696
|
+
async findPendingMints(client) {
|
|
697
|
+
const pendingMint = await client.query.mint.pendingMintUtxos();
|
|
698
|
+
const mintsPending = [];
|
|
699
|
+
for (const [utxoIdRaw, _accountId, mintAmountRaw] of pendingMint) {
|
|
700
|
+
if (utxoIdRaw.toNumber() === this.utxoId) {
|
|
701
|
+
mintsPending.push(mintAmountRaw.toBigInt());
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return mintsPending;
|
|
705
|
+
}
|
|
706
|
+
async getRatchetPrice(client, priceIndex, vault) {
|
|
707
|
+
const { createdAtHeight, vaultClaimHeight, peggedPrice, satoshis } = this;
|
|
708
|
+
const marketRate = await _BitcoinLock.getMarketRate(priceIndex, BigInt(satoshis));
|
|
709
|
+
let ratchetingFee = vault.terms.bitcoinBaseFee;
|
|
710
|
+
let burnAmount = 0n;
|
|
711
|
+
if (marketRate > peggedPrice) {
|
|
712
|
+
const lockFee = vault.calculateBitcoinFee(marketRate);
|
|
713
|
+
const currentBitcoinHeight = await client.query.bitcoinUtxos.confirmedBitcoinBlockTip().then((x) => x.unwrap().blockHeight.toNumber());
|
|
714
|
+
const blockLength = vaultClaimHeight - createdAtHeight;
|
|
715
|
+
const elapsed = (currentBitcoinHeight - createdAtHeight) / blockLength;
|
|
716
|
+
const remainingDuration = 1 - elapsed;
|
|
717
|
+
ratchetingFee = BigInt(remainingDuration * Number(lockFee));
|
|
718
|
+
} else {
|
|
719
|
+
burnAmount = await this.releasePrice(priceIndex);
|
|
720
|
+
}
|
|
721
|
+
return {
|
|
722
|
+
ratchetingFee,
|
|
723
|
+
burnAmount,
|
|
724
|
+
marketRate
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
async ratchet(args) {
|
|
728
|
+
const { priceIndex, argonKeyring, tip = 0n, vault, client } = args;
|
|
729
|
+
const ratchetPrice = await this.getRatchetPrice(client, priceIndex, vault);
|
|
730
|
+
const txSubmitter = new TxSubmitter(
|
|
731
|
+
client,
|
|
732
|
+
client.tx.bitcoinLocks.ratchet(this.utxoId),
|
|
733
|
+
argonKeyring
|
|
734
|
+
);
|
|
735
|
+
const canAfford = await txSubmitter.canAfford({
|
|
736
|
+
tip,
|
|
737
|
+
unavailableBalance: BigInt(ratchetPrice.burnAmount + ratchetPrice.ratchetingFee)
|
|
738
|
+
});
|
|
739
|
+
if (!canAfford.canAfford) {
|
|
740
|
+
throw new Error(
|
|
741
|
+
`Insufficient funds to ratchet lock. Available: ${formatArgons(canAfford.availableBalance)}, Required: ${formatArgons(
|
|
742
|
+
ratchetPrice.burnAmount + ratchetPrice.ratchetingFee
|
|
743
|
+
)}`
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
const txResult = await txSubmitter.submit(args);
|
|
747
|
+
const getRatchetResult = async () => {
|
|
748
|
+
const blockHash = await txResult.waitForFinalizedBlock;
|
|
749
|
+
const ratchetEvent = txResult.events.find(
|
|
750
|
+
(x) => client.events.bitcoinLocks.BitcoinLockRatcheted.is(x)
|
|
751
|
+
);
|
|
752
|
+
if (!ratchetEvent) {
|
|
753
|
+
throw new Error(`Ratchet event not found in transaction events`);
|
|
754
|
+
}
|
|
755
|
+
const api = await client.at(blockHash);
|
|
756
|
+
const bitcoinBlockHeight = await api.query.bitcoinUtxos.confirmedBitcoinBlockTip().then((x) => x.unwrap().blockHeight.toNumber());
|
|
757
|
+
const {
|
|
758
|
+
amountBurned,
|
|
759
|
+
liquidityPromised: liquidityPromisedRaw,
|
|
760
|
+
newPeggedPrice,
|
|
761
|
+
originalPeggedPrice,
|
|
762
|
+
securityFee
|
|
763
|
+
} = ratchetEvent.data;
|
|
764
|
+
const liquidityPromised = liquidityPromisedRaw.toBigInt();
|
|
765
|
+
let mintAmount = liquidityPromised;
|
|
766
|
+
if (liquidityPromised > originalPeggedPrice.toBigInt()) {
|
|
767
|
+
mintAmount -= originalPeggedPrice.toBigInt();
|
|
768
|
+
}
|
|
769
|
+
return {
|
|
770
|
+
txFee: txResult.finalFee ?? 0n,
|
|
771
|
+
blockHeight: txResult.blockNumber,
|
|
772
|
+
bitcoinBlockHeight,
|
|
773
|
+
pendingMint: mintAmount,
|
|
774
|
+
liquidityPromised,
|
|
775
|
+
newPeggedPrice: newPeggedPrice.toBigInt(),
|
|
776
|
+
burned: amountBurned.toBigInt(),
|
|
777
|
+
securityFee: securityFee.toBigInt()
|
|
778
|
+
};
|
|
779
|
+
};
|
|
780
|
+
return {
|
|
781
|
+
txResult,
|
|
782
|
+
getRatchetResult
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
async releasePrice(priceIndex) {
|
|
786
|
+
return await _BitcoinLock.getRedemptionRate(priceIndex, this);
|
|
647
787
|
}
|
|
648
|
-
async
|
|
788
|
+
async requestRelease(args) {
|
|
789
|
+
const {
|
|
790
|
+
priceIndex,
|
|
791
|
+
releaseRequest: { bitcoinNetworkFee, toScriptPubkey },
|
|
792
|
+
argonKeyring,
|
|
793
|
+
tip = 0n,
|
|
794
|
+
client
|
|
795
|
+
} = args;
|
|
796
|
+
if (!toScriptPubkey.startsWith("0x")) {
|
|
797
|
+
throw new Error("toScriptPubkey must be a hex string starting with 0x");
|
|
798
|
+
}
|
|
799
|
+
const submitter = new TxSubmitter(
|
|
800
|
+
client,
|
|
801
|
+
client.tx.bitcoinLocks.requestRelease(this.utxoId, toScriptPubkey, bitcoinNetworkFee),
|
|
802
|
+
argonKeyring
|
|
803
|
+
);
|
|
804
|
+
const redemptionPrice = await _BitcoinLock.getRedemptionRate(priceIndex, this);
|
|
805
|
+
const canAfford = await submitter.canAfford({
|
|
806
|
+
tip,
|
|
807
|
+
unavailableBalance: BigInt(redemptionPrice)
|
|
808
|
+
});
|
|
809
|
+
if (!canAfford.canAfford) {
|
|
810
|
+
throw new Error(
|
|
811
|
+
`Insufficient funds to release lock. Available: ${formatArgons(canAfford.availableBalance)}, Required: ${formatArgons(redemptionPrice + canAfford.txFee + tip)}`
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
return submitter.submit({
|
|
815
|
+
logResults: true,
|
|
816
|
+
...args
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
async getReleaseRequest(client) {
|
|
820
|
+
const requestMaybe = await client.query.bitcoinLocks.lockReleaseRequestsByUtxoId(this.utxoId);
|
|
821
|
+
if (!requestMaybe.isSome) {
|
|
822
|
+
return void 0;
|
|
823
|
+
}
|
|
824
|
+
const request = requestMaybe.unwrap();
|
|
825
|
+
return {
|
|
826
|
+
toScriptPubkey: request.toScriptPubkey.toHex(),
|
|
827
|
+
bitcoinNetworkFee: request.bitcoinNetworkFee.toBigInt(),
|
|
828
|
+
dueFrame: request.cosignDueFrame.toNumber(),
|
|
829
|
+
vaultId: request.vaultId.toNumber(),
|
|
830
|
+
redemptionPrice: request.redemptionPrice.toBigInt()
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Finds the cosign signature for a vault lock by UTXO ID. Optionally waits for the signature
|
|
835
|
+
* @param client - The Argon client with rpc access
|
|
836
|
+
* @param finalizedStateOnly - If true, only checks finalized state
|
|
837
|
+
* @param waitForSignatureMillis - Optional timeout in milliseconds to wait for the signature. If -1, waits indefinitely.
|
|
838
|
+
*/
|
|
839
|
+
async findVaultCosignSignature(client, finalizedStateOnly = false, waitForSignatureMillis) {
|
|
840
|
+
let queryClient = client;
|
|
841
|
+
if (finalizedStateOnly) {
|
|
842
|
+
const finalizedHead = await client.rpc.chain.getFinalizedHead();
|
|
843
|
+
queryClient = await client.at(finalizedHead);
|
|
844
|
+
}
|
|
845
|
+
const releaseHeight = await queryClient.query.bitcoinLocks.lockReleaseCosignHeightById(
|
|
846
|
+
this.utxoId
|
|
847
|
+
);
|
|
848
|
+
if (releaseHeight.isSome) {
|
|
849
|
+
const releaseHeightValue = releaseHeight.unwrap().toNumber();
|
|
850
|
+
const signature = await this.getVaultCosignSignature(client, releaseHeightValue);
|
|
851
|
+
if (signature) {
|
|
852
|
+
return { blockHeight: releaseHeightValue, signature };
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (!waitForSignatureMillis) {
|
|
856
|
+
return void 0;
|
|
857
|
+
}
|
|
858
|
+
return await new Promise(async (resolve, reject) => {
|
|
859
|
+
let timeout;
|
|
860
|
+
const unsub = await client.rpc.chain.subscribeNewHeads((header) => {
|
|
861
|
+
const atHeight = header.number.toNumber();
|
|
862
|
+
this.getVaultCosignSignature(client, atHeight).then((signature) => {
|
|
863
|
+
if (signature) {
|
|
864
|
+
unsub?.();
|
|
865
|
+
clearTimeout(timeout);
|
|
866
|
+
resolve({ signature, blockHeight: atHeight });
|
|
867
|
+
}
|
|
868
|
+
}).catch((err) => {
|
|
869
|
+
console.error(`Error checking for cosign signature at height ${atHeight}:`, err);
|
|
870
|
+
});
|
|
871
|
+
});
|
|
872
|
+
if (waitForSignatureMillis !== -1) {
|
|
873
|
+
timeout = setTimeout(() => {
|
|
874
|
+
unsub?.();
|
|
875
|
+
reject(new Error(`Timeout waiting for cosign signature for UTXO ID ${this.utxoId}`));
|
|
876
|
+
}, waitForSignatureMillis);
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
async getVaultCosignSignature(client, atHeight) {
|
|
881
|
+
const blockHash = await _BitcoinLock.blockHashAtHeight(client, atHeight);
|
|
882
|
+
if (!blockHash) {
|
|
883
|
+
console.warn(`Block hash not found for height ${atHeight}`);
|
|
884
|
+
return void 0;
|
|
885
|
+
}
|
|
886
|
+
const blockEvents = await client.at(blockHash).then((api) => api.query.system.events());
|
|
887
|
+
for (const event of blockEvents) {
|
|
888
|
+
if (client.events.bitcoinLocks.BitcoinUtxoCosigned.is(event.event)) {
|
|
889
|
+
const { utxoId: id, signature } = event.event.data;
|
|
890
|
+
if (id.toNumber() === this.utxoId) {
|
|
891
|
+
return new Uint8Array(signature);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
return void 0;
|
|
896
|
+
}
|
|
897
|
+
static async getUtxoIdFromEvents(client, events) {
|
|
649
898
|
for (const event of events) {
|
|
650
|
-
if (
|
|
899
|
+
if (client.events.bitcoinLocks.BitcoinLockCreated.is(event)) {
|
|
651
900
|
return event.data.utxoId.toNumber();
|
|
652
901
|
}
|
|
653
902
|
}
|
|
654
903
|
return void 0;
|
|
655
904
|
}
|
|
656
|
-
async getMarketRate(priceIndex, satoshis) {
|
|
905
|
+
static async getMarketRate(priceIndex, satoshis) {
|
|
657
906
|
return priceIndex.getBtcMicrogonPrice(satoshis);
|
|
658
907
|
}
|
|
659
|
-
async getRedemptionRate(priceIndex, details) {
|
|
908
|
+
static async getRedemptionRate(priceIndex, details) {
|
|
660
909
|
const { satoshis, peggedPrice } = details;
|
|
661
910
|
const satsPerArgon = Number(SATS_PER_BTC) / MICROGONS_PER_ARGON;
|
|
662
911
|
let price = Number(priceIndex.btcUsdPrice);
|
|
@@ -677,28 +926,7 @@ var BitcoinLocks = class {
|
|
|
677
926
|
}
|
|
678
927
|
return BigInt(Math.floor(price * multiplier));
|
|
679
928
|
}
|
|
680
|
-
async
|
|
681
|
-
const client = this.client;
|
|
682
|
-
const sats = client.createType("U64", satoshis.toString());
|
|
683
|
-
const marketRate = await client.rpc.state.call("BitcoinApis_market_rate", sats.toHex(true));
|
|
684
|
-
const rate = client.createType("Option<U128>", marketRate);
|
|
685
|
-
if (!rate.isSome) {
|
|
686
|
-
throw new Error("Market rate not available");
|
|
687
|
-
}
|
|
688
|
-
return rate.value.toBigInt();
|
|
689
|
-
}
|
|
690
|
-
async getRedemptionRateApi(satoshis) {
|
|
691
|
-
const client = this.client;
|
|
692
|
-
const sats = client.createType("U64", satoshis.toString());
|
|
693
|
-
const marketRate = await client.rpc.state.call("BitcoinApis_redemption_rate", sats.toHex(true));
|
|
694
|
-
const rate = client.createType("Option<U128>", marketRate);
|
|
695
|
-
if (!rate.isSome) {
|
|
696
|
-
throw new Error("Redemption rate not available");
|
|
697
|
-
}
|
|
698
|
-
return rate.value.toBigInt();
|
|
699
|
-
}
|
|
700
|
-
async getConfig() {
|
|
701
|
-
const client = this.client;
|
|
929
|
+
static async getConfig(client) {
|
|
702
930
|
const bitcoinNetwork = await client.query.bitcoinUtxos.bitcoinNetwork();
|
|
703
931
|
return {
|
|
704
932
|
lockReleaseCosignDeadlineFrames: client.consts.bitcoinLocks.lockReleaseCosignDeadlineFrames.toNumber(),
|
|
@@ -707,48 +935,11 @@ var BitcoinLocks = class {
|
|
|
707
935
|
bitcoinNetwork
|
|
708
936
|
};
|
|
709
937
|
}
|
|
710
|
-
async getBitcoinConfirmedBlockHeight() {
|
|
711
|
-
return await
|
|
712
|
-
}
|
|
713
|
-
/**
|
|
714
|
-
* Gets the UTXO reference by ID.
|
|
715
|
-
* @param utxoId - The UTXO ID to look up.
|
|
716
|
-
* @param clientAtHeight - Optional client at the block height to query the UTXO reference at a specific point in time.
|
|
717
|
-
* @return An object containing the transaction ID and output index, or undefined if not found.
|
|
718
|
-
* @return.txid - The Bitcoin transaction ID of the UTXO.
|
|
719
|
-
* @return.vout - The output index of the UTXO in the transaction.
|
|
720
|
-
* @return.bitcoinTxid - The Bitcoin transaction ID of the UTXO formatted in little endian
|
|
721
|
-
*/
|
|
722
|
-
async getUtxoRef(utxoId, clientAtHeight) {
|
|
723
|
-
const client = clientAtHeight ?? this.client;
|
|
724
|
-
const refRaw = await client.query.bitcoinUtxos.utxoIdToRef(utxoId);
|
|
725
|
-
if (!refRaw) {
|
|
726
|
-
return;
|
|
727
|
-
}
|
|
728
|
-
const ref = refRaw.unwrap();
|
|
729
|
-
const txid = u8aToHex(ref.txid);
|
|
730
|
-
const bitcoinTxid = u8aToHex(ref.txid.reverse());
|
|
731
|
-
const vout = ref.outputIndex.toNumber();
|
|
732
|
-
return { txid, vout, bitcoinTxid };
|
|
733
|
-
}
|
|
734
|
-
async getReleaseRequest(utxoId, clientAtHeight) {
|
|
735
|
-
const client = clientAtHeight ?? this.client;
|
|
736
|
-
const requestMaybe = await client.query.bitcoinLocks.lockReleaseRequestsByUtxoId(utxoId);
|
|
737
|
-
if (!requestMaybe.isSome) {
|
|
738
|
-
return void 0;
|
|
739
|
-
}
|
|
740
|
-
const request = requestMaybe.unwrap();
|
|
741
|
-
return {
|
|
742
|
-
toScriptPubkey: request.toScriptPubkey.toHex(),
|
|
743
|
-
bitcoinNetworkFee: request.bitcoinNetworkFee.toBigInt(),
|
|
744
|
-
dueFrame: request.cosignDueFrame.toNumber(),
|
|
745
|
-
vaultId: request.vaultId.toNumber(),
|
|
746
|
-
redemptionPrice: request.redemptionPrice.toBigInt()
|
|
747
|
-
};
|
|
938
|
+
static async getBitcoinConfirmedBlockHeight(client) {
|
|
939
|
+
return await client.query.bitcoinUtxos.confirmedBitcoinBlockTip().then((x) => x.value?.blockHeight.toNumber() ?? 0);
|
|
748
940
|
}
|
|
749
|
-
async submitVaultSignature(args) {
|
|
750
|
-
const { utxoId, vaultSignature, argonKeyring,
|
|
751
|
-
const client = this.client;
|
|
941
|
+
static async submitVaultSignature(args) {
|
|
942
|
+
const { utxoId, vaultSignature, argonKeyring, client } = args;
|
|
752
943
|
if (!vaultSignature || vaultSignature.byteLength < 70 || vaultSignature.byteLength > 73) {
|
|
753
944
|
throw new Error(
|
|
754
945
|
`Invalid vault signature length: ${vaultSignature.byteLength}. Must be 70-73 bytes.`
|
|
@@ -759,8 +950,8 @@ var BitcoinLocks = class {
|
|
|
759
950
|
const submitter = new TxSubmitter(client, tx, argonKeyring);
|
|
760
951
|
return await submitter.submit(args);
|
|
761
952
|
}
|
|
762
|
-
async
|
|
763
|
-
const utxoRaw = await
|
|
953
|
+
static async get(client, utxoId) {
|
|
954
|
+
const utxoRaw = await client.query.bitcoinLocks.locksByUtxoId(utxoId);
|
|
764
955
|
if (!utxoRaw.isSome) {
|
|
765
956
|
return;
|
|
766
957
|
}
|
|
@@ -791,7 +982,7 @@ var BitcoinLocks = class {
|
|
|
791
982
|
const fundHoldExtensionsByBitcoinExpirationHeight = Object.fromEntries(
|
|
792
983
|
[...utxo.fundHoldExtensions.entries()].map(([x, y]) => [x.toNumber(), y.toBigInt()])
|
|
793
984
|
);
|
|
794
|
-
return {
|
|
985
|
+
return new _BitcoinLock({
|
|
795
986
|
utxoId,
|
|
796
987
|
p2wshScriptHashHex,
|
|
797
988
|
vaultId,
|
|
@@ -810,50 +1001,9 @@ var BitcoinLocks = class {
|
|
|
810
1001
|
isVerified,
|
|
811
1002
|
isRejectedNeedsRelease,
|
|
812
1003
|
fundHoldExtensionsByBitcoinExpirationHeight
|
|
813
|
-
};
|
|
814
|
-
}
|
|
815
|
-
/**
|
|
816
|
-
* Finds the cosign signature for a vault lock by UTXO ID. Optionally waits for the signature
|
|
817
|
-
* @param utxoId - The UTXO ID of the bitcoin lock
|
|
818
|
-
* @param waitForSignatureMillis - Optional timeout in milliseconds to wait for the signature. If -1, waits indefinitely.
|
|
819
|
-
*/
|
|
820
|
-
async findVaultCosignSignature(utxoId, waitForSignatureMillis) {
|
|
821
|
-
const client = this.client;
|
|
822
|
-
const releaseHeight = await client.query.bitcoinLocks.lockReleaseCosignHeightById(utxoId);
|
|
823
|
-
if (releaseHeight.isSome) {
|
|
824
|
-
const releaseHeightValue = releaseHeight.unwrap().toNumber();
|
|
825
|
-
const signature = await this.getVaultCosignSignature(utxoId, releaseHeightValue);
|
|
826
|
-
if (signature) {
|
|
827
|
-
return { blockHeight: releaseHeightValue, signature };
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
if (!waitForSignatureMillis) {
|
|
831
|
-
return void 0;
|
|
832
|
-
}
|
|
833
|
-
return await new Promise(async (resolve, reject) => {
|
|
834
|
-
let timeout;
|
|
835
|
-
const unsub = await client.rpc.chain.subscribeNewHeads((header) => {
|
|
836
|
-
const atHeight = header.number.toNumber();
|
|
837
|
-
this.getVaultCosignSignature(utxoId, atHeight).then((signature) => {
|
|
838
|
-
if (signature) {
|
|
839
|
-
unsub?.();
|
|
840
|
-
clearTimeout(timeout);
|
|
841
|
-
resolve({ signature, blockHeight: atHeight });
|
|
842
|
-
}
|
|
843
|
-
}).catch((err) => {
|
|
844
|
-
console.error(`Error checking for cosign signature at height ${atHeight}:`, err);
|
|
845
|
-
});
|
|
846
|
-
});
|
|
847
|
-
if (waitForSignatureMillis !== -1) {
|
|
848
|
-
timeout = setTimeout(() => {
|
|
849
|
-
unsub?.();
|
|
850
|
-
reject(new Error(`Timeout waiting for cosign signature for UTXO ID ${utxoId}`));
|
|
851
|
-
}, waitForSignatureMillis);
|
|
852
|
-
}
|
|
853
1004
|
});
|
|
854
1005
|
}
|
|
855
|
-
async blockHashAtHeight(atHeight) {
|
|
856
|
-
const client = this.client;
|
|
1006
|
+
static async blockHashAtHeight(client, atHeight) {
|
|
857
1007
|
for (let i = 0; i < 10; i++) {
|
|
858
1008
|
const currentHeight = await client.query.system.number().then((x) => x.toNumber());
|
|
859
1009
|
if (atHeight > currentHeight) {
|
|
@@ -873,37 +1023,16 @@ var BitcoinLocks = class {
|
|
|
873
1023
|
}
|
|
874
1024
|
return void 0;
|
|
875
1025
|
}
|
|
876
|
-
async
|
|
877
|
-
const
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
const { utxoId: id, signature } = event.event.data;
|
|
887
|
-
if (id.toNumber() === utxoId) {
|
|
888
|
-
return new Uint8Array(signature);
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
return void 0;
|
|
893
|
-
}
|
|
894
|
-
async findPendingMints(utxoId) {
|
|
895
|
-
const pendingMint = await this.client.query.mint.pendingMintUtxos();
|
|
896
|
-
const mintsPending = [];
|
|
897
|
-
for (const [utxoIdRaw, _accountId, mintAmountRaw] of pendingMint) {
|
|
898
|
-
if (utxoIdRaw.toNumber() === utxoId) {
|
|
899
|
-
mintsPending.push(mintAmountRaw.toBigInt());
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
return mintsPending;
|
|
903
|
-
}
|
|
904
|
-
async createInitializeLockTx(args) {
|
|
905
|
-
const { vault, priceIndex, argonKeyring, satoshis, tip = 0n, ownerBitcoinPubkey } = args;
|
|
906
|
-
const client = this.client;
|
|
1026
|
+
static async createInitializeTx(args) {
|
|
1027
|
+
const {
|
|
1028
|
+
vault,
|
|
1029
|
+
priceIndex,
|
|
1030
|
+
argonKeyring,
|
|
1031
|
+
satoshis,
|
|
1032
|
+
tip = 0n,
|
|
1033
|
+
ownerBitcoinPubkey,
|
|
1034
|
+
client
|
|
1035
|
+
} = args;
|
|
907
1036
|
if (ownerBitcoinPubkey.length !== 33) {
|
|
908
1037
|
throw new Error(
|
|
909
1038
|
`Invalid Bitcoin key length: ${ownerBitcoinPubkey.length}. Must be a compressed pukey (33 bytes).`
|
|
@@ -925,23 +1054,9 @@ var BitcoinLocks = class {
|
|
|
925
1054
|
});
|
|
926
1055
|
return { tx, securityFee, txFee, canAfford, availableBalance, txFeePlusTip: txFee + tip };
|
|
927
1056
|
}
|
|
928
|
-
async
|
|
929
|
-
|
|
930
|
-
const
|
|
931
|
-
const utxoId = await this.getUtxoIdFromEvents(txResult.events) ?? 0;
|
|
932
|
-
if (utxoId === 0) {
|
|
933
|
-
throw new Error("Bitcoin lock creation failed, no UTXO ID found in transaction events");
|
|
934
|
-
}
|
|
935
|
-
const lock = await this.getBitcoinLock(utxoId);
|
|
936
|
-
if (!lock) {
|
|
937
|
-
throw new Error(`Lock with ID ${utxoId} not found after initialization`);
|
|
938
|
-
}
|
|
939
|
-
return { lock, createdAtHeight: blockHeight, txResult };
|
|
940
|
-
}
|
|
941
|
-
async initializeLock(args) {
|
|
942
|
-
const { argonKeyring } = args;
|
|
943
|
-
const client = this.client;
|
|
944
|
-
const { tx, securityFee, canAfford, txFeePlusTip } = await this.createInitializeLockTx(args);
|
|
1057
|
+
static async initialize(args) {
|
|
1058
|
+
const { argonKeyring, client } = args;
|
|
1059
|
+
const { tx, securityFee, canAfford, txFeePlusTip } = await this.createInitializeTx(args);
|
|
945
1060
|
if (!canAfford) {
|
|
946
1061
|
throw new Error(
|
|
947
1062
|
`Insufficient funds to initialize bitcoin lock. Required security fee: ${formatArgons(securityFee)}, Tx fee plus tip: ${formatArgons(txFeePlusTip)}`
|
|
@@ -950,130 +1065,27 @@ var BitcoinLocks = class {
|
|
|
950
1065
|
const submitter = new TxSubmitter(client, tx, argonKeyring);
|
|
951
1066
|
const txResult = await submitter.submit({ logResults: true, ...args });
|
|
952
1067
|
return {
|
|
953
|
-
getLock: () => this.getBitcoinLockFromTxResult(txResult),
|
|
1068
|
+
getLock: () => this.getBitcoinLockFromTxResult(client, txResult),
|
|
954
1069
|
txResult,
|
|
955
1070
|
securityFee
|
|
956
1071
|
};
|
|
957
1072
|
}
|
|
958
|
-
async
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
const {
|
|
965
|
-
lock,
|
|
966
|
-
priceIndex,
|
|
967
|
-
releaseRequest: { bitcoinNetworkFee, toScriptPubkey },
|
|
968
|
-
argonKeyring,
|
|
969
|
-
tip = 0n
|
|
970
|
-
} = args;
|
|
971
|
-
if (!toScriptPubkey.startsWith("0x")) {
|
|
972
|
-
throw new Error("toScriptPubkey must be a hex string starting with 0x");
|
|
973
|
-
}
|
|
974
|
-
const submitter = new TxSubmitter(
|
|
975
|
-
client,
|
|
976
|
-
client.tx.bitcoinLocks.requestRelease(lock.utxoId, toScriptPubkey, bitcoinNetworkFee),
|
|
977
|
-
argonKeyring
|
|
978
|
-
);
|
|
979
|
-
const redemptionPrice = await this.getRedemptionRate(priceIndex, lock);
|
|
980
|
-
const canAfford = await submitter.canAfford({
|
|
981
|
-
tip,
|
|
982
|
-
unavailableBalance: BigInt(redemptionPrice)
|
|
983
|
-
});
|
|
984
|
-
if (!canAfford.canAfford) {
|
|
985
|
-
throw new Error(
|
|
986
|
-
`Insufficient funds to release lock. Available: ${formatArgons(canAfford.availableBalance)}, Required: ${formatArgons(redemptionPrice + canAfford.txFee + tip)}`
|
|
987
|
-
);
|
|
1073
|
+
static async getBitcoinLockFromTxResult(client, txResult) {
|
|
1074
|
+
await txResult.waitForFinalizedBlock;
|
|
1075
|
+
const blockHeight = txResult.blockNumber;
|
|
1076
|
+
const utxoId = await this.getUtxoIdFromEvents(client, txResult.events) ?? 0;
|
|
1077
|
+
if (utxoId === 0) {
|
|
1078
|
+
throw new Error("Bitcoin lock creation failed, no UTXO ID found in transaction events");
|
|
988
1079
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
});
|
|
993
|
-
}
|
|
994
|
-
async releasePrice(priceIndex, lock) {
|
|
995
|
-
return await this.getRedemptionRate(priceIndex, lock);
|
|
996
|
-
}
|
|
997
|
-
async getRatchetPrice(lock, priceIndex, vault) {
|
|
998
|
-
const { createdAtHeight, vaultClaimHeight, peggedPrice, satoshis } = lock;
|
|
999
|
-
const client = this.client;
|
|
1000
|
-
const marketRate = await this.getMarketRate(priceIndex, BigInt(satoshis));
|
|
1001
|
-
let ratchetingFee = vault.terms.bitcoinBaseFee;
|
|
1002
|
-
let burnAmount = 0n;
|
|
1003
|
-
if (marketRate > peggedPrice) {
|
|
1004
|
-
const lockFee = vault.calculateBitcoinFee(marketRate);
|
|
1005
|
-
const currentBitcoinHeight = await client.query.bitcoinUtxos.confirmedBitcoinBlockTip().then((x) => x.unwrap().blockHeight.toNumber());
|
|
1006
|
-
const blockLength = vaultClaimHeight - createdAtHeight;
|
|
1007
|
-
const elapsed = (currentBitcoinHeight - createdAtHeight) / blockLength;
|
|
1008
|
-
const remainingDuration = 1 - elapsed;
|
|
1009
|
-
ratchetingFee = BigInt(remainingDuration * Number(lockFee));
|
|
1010
|
-
} else {
|
|
1011
|
-
burnAmount = await this.releasePrice(priceIndex, lock);
|
|
1080
|
+
const lock = await this.get(client, utxoId);
|
|
1081
|
+
if (!lock) {
|
|
1082
|
+
throw new Error(`Lock with ID ${utxoId} not found after initialization`);
|
|
1012
1083
|
}
|
|
1013
|
-
return {
|
|
1014
|
-
ratchetingFee,
|
|
1015
|
-
burnAmount,
|
|
1016
|
-
marketRate
|
|
1017
|
-
};
|
|
1084
|
+
return { lock, createdAtHeight: blockHeight, txResult };
|
|
1018
1085
|
}
|
|
1019
|
-
async
|
|
1020
|
-
const
|
|
1021
|
-
|
|
1022
|
-
const ratchetPrice = await this.getRatchetPrice(lock, priceIndex, vault);
|
|
1023
|
-
const txSubmitter = new TxSubmitter(
|
|
1024
|
-
client,
|
|
1025
|
-
client.tx.bitcoinLocks.ratchet(lock.utxoId),
|
|
1026
|
-
argonKeyring
|
|
1027
|
-
);
|
|
1028
|
-
const canAfford = await txSubmitter.canAfford({
|
|
1029
|
-
tip,
|
|
1030
|
-
unavailableBalance: BigInt(ratchetPrice.burnAmount + ratchetPrice.ratchetingFee)
|
|
1031
|
-
});
|
|
1032
|
-
if (!canAfford.canAfford) {
|
|
1033
|
-
throw new Error(
|
|
1034
|
-
`Insufficient funds to ratchet lock. Available: ${formatArgons(canAfford.availableBalance)}, Required: ${formatArgons(
|
|
1035
|
-
ratchetPrice.burnAmount + ratchetPrice.ratchetingFee
|
|
1036
|
-
)}`
|
|
1037
|
-
);
|
|
1038
|
-
}
|
|
1039
|
-
const txResult = await txSubmitter.submit(args);
|
|
1040
|
-
const getRatchetResult = async () => {
|
|
1041
|
-
const blockHash = await txResult.waitForFinalizedBlock;
|
|
1042
|
-
const ratchetEvent = txResult.events.find(
|
|
1043
|
-
(x) => client.events.bitcoinLocks.BitcoinLockRatcheted.is(x)
|
|
1044
|
-
);
|
|
1045
|
-
if (!ratchetEvent) {
|
|
1046
|
-
throw new Error(`Ratchet event not found in transaction events`);
|
|
1047
|
-
}
|
|
1048
|
-
const api = await client.at(blockHash);
|
|
1049
|
-
const bitcoinBlockHeight = await api.query.bitcoinUtxos.confirmedBitcoinBlockTip().then((x) => x.unwrap().blockHeight.toNumber());
|
|
1050
|
-
const {
|
|
1051
|
-
amountBurned,
|
|
1052
|
-
liquidityPromised: liquidityPromisedRaw,
|
|
1053
|
-
newPeggedPrice,
|
|
1054
|
-
originalPeggedPrice,
|
|
1055
|
-
securityFee
|
|
1056
|
-
} = ratchetEvent.data;
|
|
1057
|
-
const liquidityPromised = liquidityPromisedRaw.toBigInt();
|
|
1058
|
-
let mintAmount = liquidityPromised;
|
|
1059
|
-
if (liquidityPromised > originalPeggedPrice.toBigInt()) {
|
|
1060
|
-
mintAmount -= originalPeggedPrice.toBigInt();
|
|
1061
|
-
}
|
|
1062
|
-
return {
|
|
1063
|
-
txFee: txResult.finalFee ?? 0n,
|
|
1064
|
-
blockHeight: txResult.blockNumber,
|
|
1065
|
-
bitcoinBlockHeight,
|
|
1066
|
-
pendingMint: mintAmount,
|
|
1067
|
-
liquidityPromised,
|
|
1068
|
-
newPeggedPrice: newPeggedPrice.toBigInt(),
|
|
1069
|
-
burned: amountBurned.toBigInt(),
|
|
1070
|
-
securityFee: securityFee.toBigInt()
|
|
1071
|
-
};
|
|
1072
|
-
};
|
|
1073
|
-
return {
|
|
1074
|
-
txResult,
|
|
1075
|
-
getRatchetResult
|
|
1076
|
-
};
|
|
1086
|
+
static async requiredSatoshisForArgonLiquidity(priceIndex, argonAmount) {
|
|
1087
|
+
const marketRatePerBitcoin = priceIndex.getBtcMicrogonPrice(SATS_PER_BTC);
|
|
1088
|
+
return argonAmount * SATS_PER_BTC / marketRatePerBitcoin;
|
|
1077
1089
|
}
|
|
1078
1090
|
};
|
|
1079
1091
|
var PriceIndex = class {
|
|
@@ -1146,6 +1158,6 @@ async function getClient(host, options) {
|
|
|
1146
1158
|
return await ApiPromise.create({ provider, noInitWarn: true, ...options ?? {} });
|
|
1147
1159
|
}
|
|
1148
1160
|
|
|
1149
|
-
export {
|
|
1161
|
+
export { BitcoinLock, ExtrinsicError, FIXED_U128_DECIMALS, MICROGONS_PER_ARGON, PERMILL_DECIMALS, PriceIndex, SATS_PER_BTC, TxResult, TxSubmitter, Vault, WageProtector, checkForExtrinsicSuccess, createKeyringPair, dispatchErrorToExtrinsicError, dispatchErrorToString, formatArgons, fromFixedNumber, getAuthorFromHeader, getClient, getTickFromHeader, gettersToObject, keyringFromSuri, toFixedNumber, waitForLoad };
|
|
1150
1162
|
//# sourceMappingURL=index.js.map
|
|
1151
1163
|
//# sourceMappingURL=index.js.map
|