@oyl/sdk
Version:
476 lines • 18.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.actualSplitFee = exports.createSplitPsbt = exports.split = exports.actualSendFee = exports.send = exports.createSendPsbt = exports.tokenDeployment = void 0;
const tslib_1 = require("tslib");
const btc_1 = require("../btc");
const integer_1 = require("@magiceden-oss/runestone-lib/dist/src/integer");
const index_js_1 = require("alkanes/lib/index.js");
const protoruneruneid_1 = require("alkanes/lib/protorune/protoruneruneid");
const errors_1 = require("../errors");
const bitcoin = tslib_1.__importStar(require("bitcoinjs-lib"));
const utils_1 = require("../shared/utils");
const psbt_1 = require("../psbt");
const alkanes_1 = require("./alkanes");
const tokenDeployment = async ({ payload, gatheredUtxos, account, protostone, provider, feeRate, signer, }) => {
const { script, txId } = await (0, alkanes_1.deployCommit)({
payload,
gatheredUtxos,
account,
provider,
feeRate,
signer,
});
await (0, utils_1.timeout)(3000);
const reveal = await (0, alkanes_1.deployReveal)({
protostone,
script,
commitTxId: txId,
account,
provider,
feeRate,
signer,
});
return { ...reveal, commitTx: txId };
};
exports.tokenDeployment = tokenDeployment;
const createSendPsbt = async ({ gatheredUtxos, account, alkaneId, provider, toAddress, amount, feeRate, fee, }) => {
try {
const originalGatheredUtxos = gatheredUtxos;
const minFee = (0, btc_1.minimumFee)({
taprootInputCount: 2,
nonTaprootInputCount: 0,
outputCount: 3,
});
const calculatedFee = minFee * feeRate < 250 ? 250 : minFee * feeRate;
let finalFee = fee ? fee : calculatedFee;
gatheredUtxos = (0, utils_1.findXAmountOfSats)(originalGatheredUtxos.utxos, Number(finalFee) + Number(utils_1.inscriptionSats));
if (gatheredUtxos.utxos.length > 1) {
const txSize = (0, btc_1.minimumFee)({
taprootInputCount: gatheredUtxos.utxos.length,
nonTaprootInputCount: 0,
outputCount: 3,
});
finalFee = Math.max(txSize * feeRate, 250);
gatheredUtxos = (0, utils_1.findXAmountOfSats)(originalGatheredUtxos.utxos, Number(finalFee) + Number(utils_1.inscriptionSats));
}
let psbt = new bitcoin.Psbt({ network: provider.network });
const { alkaneUtxos, totalSatoshis } = await (0, alkanes_1.findAlkaneUtxos)({
address: account.taproot.address,
greatestToLeast: account.spendStrategy.utxoSortGreatestToLeast,
alkaneId,
provider,
targetNumberOfAlkanes: amount,
});
if (alkaneUtxos.length === 0) {
throw new errors_1.OylTransactionError(Error('No Alkane Utxos Found'));
}
for await (const utxo of alkaneUtxos) {
if ((0, utils_1.getAddressType)(utxo.address) === 0) {
const previousTxHex = await provider.esplora.getTxHex(utxo.txId);
psbt.addInput({
hash: utxo.txId,
index: utxo.txIndex,
nonWitnessUtxo: Buffer.from(previousTxHex, 'hex'),
});
}
if ((0, utils_1.getAddressType)(utxo.address) === 2) {
const redeemScript = bitcoin.script.compile([
bitcoin.opcodes.OP_0,
bitcoin.crypto.hash160(Buffer.from(account.nestedSegwit.pubkey, 'hex')),
]);
psbt.addInput({
hash: utxo.txId,
index: utxo.txIndex,
redeemScript: redeemScript,
witnessUtxo: {
value: utxo.satoshis,
script: bitcoin.script.compile([
bitcoin.opcodes.OP_HASH160,
bitcoin.crypto.hash160(redeemScript),
bitcoin.opcodes.OP_EQUAL,
]),
},
});
}
if ((0, utils_1.getAddressType)(utxo.address) === 1 ||
(0, utils_1.getAddressType)(utxo.address) === 3) {
psbt.addInput({
hash: utxo.txId,
index: utxo.txIndex,
witnessUtxo: {
value: utxo.satoshis,
script: Buffer.from(utxo.script, 'hex'),
},
});
}
}
if (gatheredUtxos.totalAmount < finalFee + utils_1.inscriptionSats * 2) {
throw new errors_1.OylTransactionError(Error('Insufficient Balance'));
}
for (let i = 0; i < gatheredUtxos.utxos.length; i++) {
if ((0, utils_1.getAddressType)(gatheredUtxos.utxos[i].address) === 0) {
const previousTxHex = await provider.esplora.getTxHex(gatheredUtxos.utxos[i].txId);
psbt.addInput({
hash: gatheredUtxos.utxos[i].txId,
index: gatheredUtxos.utxos[i].outputIndex,
nonWitnessUtxo: Buffer.from(previousTxHex, 'hex'),
});
}
if ((0, utils_1.getAddressType)(gatheredUtxos.utxos[i].address) === 2) {
const redeemScript = bitcoin.script.compile([
bitcoin.opcodes.OP_0,
bitcoin.crypto.hash160(Buffer.from(account.nestedSegwit.pubkey, 'hex')),
]);
psbt.addInput({
hash: gatheredUtxos.utxos[i].txId,
index: gatheredUtxos.utxos[i].outputIndex,
redeemScript: redeemScript,
witnessUtxo: {
value: gatheredUtxos.utxos[i].satoshis,
script: bitcoin.script.compile([
bitcoin.opcodes.OP_HASH160,
bitcoin.crypto.hash160(redeemScript),
bitcoin.opcodes.OP_EQUAL,
]),
},
});
}
if ((0, utils_1.getAddressType)(gatheredUtxos.utxos[i].address) === 1 ||
(0, utils_1.getAddressType)(gatheredUtxos.utxos[i].address) === 3) {
psbt.addInput({
hash: gatheredUtxos.utxos[i].txId,
index: gatheredUtxos.utxos[i].outputIndex,
witnessUtxo: {
value: gatheredUtxos.utxos[i].satoshis,
script: Buffer.from(gatheredUtxos.utxos[i].scriptPk, 'hex'),
},
});
}
}
const protostone = (0, index_js_1.encodeRunestoneProtostone)({
protostones: [
index_js_1.ProtoStone.message({
protocolTag: 1n,
edicts: [
{
id: new protoruneruneid_1.ProtoruneRuneId((0, integer_1.u128)(BigInt(alkaneId.block)), (0, integer_1.u128)(BigInt(alkaneId.tx))),
amount: (0, integer_1.u128)(BigInt(amount)),
output: (0, integer_1.u32)(BigInt(1)),
},
],
pointer: 0,
refundPointer: 0,
calldata: Buffer.from([]),
}),
],
}).encodedRunestone;
psbt.addOutput({
value: utils_1.inscriptionSats,
address: account.taproot.address,
});
psbt.addOutput({
value: utils_1.inscriptionSats,
address: toAddress,
});
const output = { script: protostone, value: 0 };
psbt.addOutput(output);
const changeAmount = gatheredUtxos.totalAmount +
totalSatoshis -
(finalFee + utils_1.inscriptionSats * 2);
psbt.addOutput({
address: account[account.spendStrategy.changeAddress].address,
value: changeAmount,
});
const formattedPsbtTx = await (0, utils_1.formatInputsToSign)({
_psbt: psbt,
senderPublicKey: account.taproot.pubkey,
network: provider.network,
});
return { psbt: formattedPsbtTx.toBase64() };
}
catch (error) {
throw new errors_1.OylTransactionError(error);
}
};
exports.createSendPsbt = createSendPsbt;
const send = async ({ gatheredUtxos, toAddress, amount, alkaneId, feeRate, account, provider, signer, }) => {
const { fee } = await (0, exports.actualSendFee)({
gatheredUtxos,
account,
alkaneId,
amount,
provider,
toAddress,
feeRate,
});
const { psbt: finalPsbt } = await (0, exports.createSendPsbt)({
gatheredUtxos,
account,
alkaneId,
amount,
provider,
toAddress,
feeRate,
fee,
});
const { signedPsbt } = await signer.signAllInputs({
rawPsbt: finalPsbt,
finalize: true,
});
const result = await provider.pushPsbt({
psbtBase64: signedPsbt,
});
return result;
};
exports.send = send;
const actualSendFee = async ({ gatheredUtxos, account, alkaneId, provider, toAddress, amount, feeRate, }) => {
if (!feeRate) {
feeRate = (await provider.esplora.getFeeEstimates())['1'];
}
const { psbt } = await (0, exports.createSendPsbt)({
gatheredUtxos,
account,
alkaneId,
provider,
toAddress,
amount,
feeRate,
});
const { fee: estimatedFee } = await (0, psbt_1.getEstimatedFee)({
feeRate,
psbt,
provider,
});
const { psbt: finalPsbt } = await (0, exports.createSendPsbt)({
gatheredUtxos,
account,
alkaneId,
provider,
toAddress,
amount,
feeRate,
fee: estimatedFee,
});
const { fee: finalFee, vsize } = await (0, psbt_1.getEstimatedFee)({
feeRate,
psbt: finalPsbt,
provider,
});
return { fee: finalFee, vsize };
};
exports.actualSendFee = actualSendFee;
const split = async ({ alkaneUtxos, gatheredUtxos, account, protostone, provider, feeRate, signer, }) => {
const { fee } = await (0, exports.actualSplitFee)({
alkaneUtxos,
gatheredUtxos,
account,
protostone,
provider,
feeRate,
signer,
});
const { psbt: finalPsbt } = await (0, exports.createSplitPsbt)({
alkaneUtxos,
gatheredUtxos,
account,
protostone,
provider,
feeRate,
fee,
});
const { signedPsbt } = await signer.signAllInputs({
rawPsbt: finalPsbt,
finalize: true,
});
const revealResult = await provider.pushPsbt({
psbtBase64: signedPsbt,
});
return revealResult;
};
exports.split = split;
const createSplitPsbt = async ({ alkaneUtxos, gatheredUtxos, account, protostone, provider, feeRate, fee = 0, }) => {
try {
const originalGatheredUtxos = gatheredUtxos;
const minTxSize = (0, btc_1.minimumFee)({
taprootInputCount: 2,
nonTaprootInputCount: 0,
outputCount: 2,
});
let calculatedFee = Math.max(minTxSize * feeRate, 250);
let finalFee = fee === 0 ? calculatedFee : fee;
gatheredUtxos = (0, utils_1.findXAmountOfSats)(originalGatheredUtxos.utxos, Number(finalFee) + 546 * alkaneUtxos.alkaneUtxos.length * 2);
let psbt = new bitcoin.Psbt({ network: provider.network });
if (alkaneUtxos) {
for await (const utxo of alkaneUtxos.alkaneUtxos) {
if ((0, utils_1.getAddressType)(utxo.address) === 0) {
const previousTxHex = await provider.esplora.getTxHex(utxo.txId);
psbt.addInput({
hash: utxo.txId,
index: parseInt(utxo.txIndex),
nonWitnessUtxo: Buffer.from(previousTxHex, 'hex'),
});
}
if ((0, utils_1.getAddressType)(utxo.address) === 2) {
const redeemScript = bitcoin.script.compile([
bitcoin.opcodes.OP_0,
bitcoin.crypto.hash160(Buffer.from(account.nestedSegwit.pubkey, 'hex')),
]);
psbt.addInput({
hash: utxo.txId,
index: parseInt(utxo.txIndex),
redeemScript: redeemScript,
witnessUtxo: {
value: utxo.satoshis,
script: bitcoin.script.compile([
bitcoin.opcodes.OP_HASH160,
bitcoin.crypto.hash160(redeemScript),
bitcoin.opcodes.OP_EQUAL,
]),
},
});
}
if ((0, utils_1.getAddressType)(utxo.address) === 1 ||
(0, utils_1.getAddressType)(utxo.address) === 3) {
psbt.addInput({
hash: utxo.txId,
index: parseInt(utxo.txIndex),
witnessUtxo: {
value: utxo.satoshis,
script: Buffer.from(utxo.script, 'hex'),
},
});
}
}
}
if (fee === 0 && gatheredUtxos.utxos.length > 1) {
const txSize = (0, btc_1.minimumFee)({
taprootInputCount: gatheredUtxos.utxos.length,
nonTaprootInputCount: 0,
outputCount: 2,
});
finalFee = txSize * feeRate < 250 ? 250 : txSize * feeRate;
if (gatheredUtxos.totalAmount < finalFee) {
throw new errors_1.OylTransactionError(Error('Insufficient Balance'));
}
}
if (gatheredUtxos.totalAmount < finalFee) {
throw new errors_1.OylTransactionError(Error('Insufficient Balance'));
}
for (let i = 0; i < gatheredUtxos.utxos.length; i++) {
if ((0, utils_1.getAddressType)(gatheredUtxos.utxos[i].address) === 0) {
const previousTxHex = await provider.esplora.getTxHex(gatheredUtxos.utxos[i].txId);
psbt.addInput({
hash: gatheredUtxos.utxos[i].txId,
index: gatheredUtxos.utxos[i].outputIndex,
nonWitnessUtxo: Buffer.from(previousTxHex, 'hex'),
});
}
if ((0, utils_1.getAddressType)(gatheredUtxos.utxos[i].address) === 2) {
const redeemScript = bitcoin.script.compile([
bitcoin.opcodes.OP_0,
bitcoin.crypto.hash160(Buffer.from(account.nestedSegwit.pubkey, 'hex')),
]);
psbt.addInput({
hash: gatheredUtxos.utxos[i].txId,
index: gatheredUtxos.utxos[i].outputIndex,
redeemScript: redeemScript,
witnessUtxo: {
value: gatheredUtxos.utxos[i].satoshis,
script: bitcoin.script.compile([
bitcoin.opcodes.OP_HASH160,
bitcoin.crypto.hash160(redeemScript),
bitcoin.opcodes.OP_EQUAL,
]),
},
});
}
if ((0, utils_1.getAddressType)(gatheredUtxos.utxos[i].address) === 1 ||
(0, utils_1.getAddressType)(gatheredUtxos.utxos[i].address) === 3) {
psbt.addInput({
hash: gatheredUtxos.utxos[i].txId,
index: gatheredUtxos.utxos[i].outputIndex,
witnessUtxo: {
value: gatheredUtxos.utxos[i].satoshis,
script: Buffer.from(gatheredUtxos.utxos[i].scriptPk, 'hex'),
},
});
}
}
for (let i = 0; i < alkaneUtxos.alkaneUtxos.length * 2; i++) {
psbt.addOutput({
address: account.taproot.address,
value: 546,
});
}
const output = { script: protostone, value: 0 };
psbt.addOutput(output);
const changeAmount = gatheredUtxos.totalAmount +
(alkaneUtxos?.totalSatoshis || 0) -
finalFee -
546 * alkaneUtxos.alkaneUtxos.length * 2;
psbt.addOutput({
address: account[account.spendStrategy.changeAddress].address,
value: changeAmount,
});
const formattedPsbtTx = await (0, utils_1.formatInputsToSign)({
_psbt: psbt,
senderPublicKey: account.taproot.pubkey,
network: provider.network,
});
return {
psbt: formattedPsbtTx.toBase64(),
psbtHex: formattedPsbtTx.toHex(),
};
}
catch (error) {
throw new errors_1.OylTransactionError(error);
}
};
exports.createSplitPsbt = createSplitPsbt;
const actualSplitFee = async ({ gatheredUtxos, account, protostone, provider, feeRate, signer, alkaneUtxos, }) => {
if (!feeRate) {
feeRate = (await provider.esplora.getFeeEstimates())['1'];
}
const { psbt } = await (0, exports.createSplitPsbt)({
gatheredUtxos,
account,
protostone,
provider,
feeRate,
alkaneUtxos,
});
const { signedPsbt } = await signer.signAllInputs({
rawPsbt: psbt,
finalize: true,
});
let rawPsbt = bitcoin.Psbt.fromBase64(signedPsbt, {
network: account.network,
})
.extractTransaction()
.toHex();
const vsize = (await provider.sandshrew.bitcoindRpc.testMemPoolAccept([rawPsbt]))[0].vsize;
const correctFee = vsize * feeRate;
const { psbt: finalPsbt } = await (0, exports.createSplitPsbt)({
gatheredUtxos,
account,
protostone,
provider,
feeRate,
alkaneUtxos,
fee: correctFee,
});
const { signedPsbt: finalSignedPsbt } = await signer.signAllInputs({
rawPsbt: finalPsbt,
finalize: true,
});
let finalRawPsbt = bitcoin.Psbt.fromBase64(finalSignedPsbt, {
network: account.network,
})
.extractTransaction()
.toHex();
const finalVsize = (await provider.sandshrew.bitcoindRpc.testMemPoolAccept([finalRawPsbt]))[0].vsize;
const finalFee = finalVsize * feeRate;
return { fee: finalFee };
};
exports.actualSplitFee = actualSplitFee;
//# sourceMappingURL=token.js.map