@okxweb3/coin-bitcoin
Version:
@okxweb3/coin-bitcoin is a Bitcoin SDK for building Web3 wallets and applications. It supports BTC, BSV, DOGE, LTC, and TBTC, enabling private key management, transaction signing, address generation, and inscriptions like BRC-20, Runes, CAT, and Atomicals
155 lines • 6.99 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.unlockGuard = exports.unlockToken = exports.createGuardContract = void 0;
const utils_1 = require("../utils");
const common_1 = require("../common");
const utils_2 = require("../utils");
const cat_smartcontracts_1 = require("@cat-protocol/cat-smartcontracts");
const scrypt_ts_1 = require("scrypt-ts");
function createGuardContract(ecKey, feeutxo, feeRate, tokens, tokenP2TR, changeAddress) {
const { p2tr: guardP2TR, tapScript: guardTapScript } = (0, utils_2.getGuardsP2TR)();
const protocolState = cat_smartcontracts_1.ProtocolState.getEmptyState();
const realState = cat_smartcontracts_1.GuardProto.createEmptyState();
realState.tokenScript = tokenP2TR;
for (let i = 0; i < tokens.length; i++) {
realState.inputTokenAmountArray[i] = tokens[i].state.data.amount;
}
protocolState.updateDataList(0, cat_smartcontracts_1.GuardProto.toByteString(realState));
const commitTx = new common_1.btc.Transaction()
.from(feeutxo)
.addOutput(new common_1.btc.Transaction.Output({
satoshis: 0,
script: (0, utils_2.toStateScript)(protocolState),
}))
.addOutput(new common_1.btc.Transaction.Output({
satoshis: common_1.Postage.GUARD_POSTAGE,
script: guardP2TR,
}))
.feePerByte(feeRate)
.change(changeAddress);
if (commitTx.getChangeOutput() === null) {
throw new Error('Insufficient satoshis balance!');
}
commitTx.outputs[2].satoshis -= 1;
if (ecKey.hasPrivateKey()) {
ecKey.signTx(commitTx);
}
const contact = {
utxo: {
txId: commitTx.id,
outputIndex: 1,
script: commitTx.outputs[1].script.toHex(),
satoshis: commitTx.outputs[1].satoshis,
},
state: {
protocolState,
data: realState,
},
};
return {
commitTx,
contact,
guardTapScript,
};
}
exports.createGuardContract = createGuardContract;
async function unlockToken(ecKey, tokenContract, tokenInputIndex, prevTokenTx, preTokenInputIndex, prevPrevTokenTx, guardInfo, revealTx, minterP2TR, txCtx, verify, signature, contractSpend, contractInputIndex) {
const { cblock: cblockToken, contract: token } = (0, utils_2.getTokenContractP2TR)(minterP2TR);
const { shPreimage, prevoutsCtx, spentScripts, sighash } = txCtx;
let tokenUnlockArgs = {
isUserSpend: false,
userPubKeyPrefix: (0, scrypt_ts_1.toByteString)(''),
userPubKey: (0, scrypt_ts_1.PubKey)(ecKey.getXOnlyPublicKey()),
userSig: (0, utils_1.getDummySignature)(),
contractInputIndex: BigInt(contractInputIndex || 0),
};
if (!contractSpend) {
let sig;
if (ecKey.hasPrivateKey()) {
sig = common_1.btc.crypto.Schnorr.sign(ecKey.getTokenPrivateKey(), sighash.hash);
}
else {
sig = common_1.btc.crypto.Signature.fromString(signature);
}
const pubkeyX = ecKey.getXOnlyPublicKey();
const pubKeyPrefix = ecKey.getPubKeyPrefix();
tokenUnlockArgs = {
isUserSpend: true,
userPubKeyPrefix: pubKeyPrefix,
userPubKey: (0, scrypt_ts_1.PubKey)(pubkeyX),
userSig: sig.toString('hex'),
contractInputIndex: 0n,
};
}
const backtraceInfo = (0, cat_smartcontracts_1.getBackTraceInfo)(prevTokenTx, prevPrevTokenTx, preTokenInputIndex);
const { state: { protocolState, data: preState }, } = tokenContract;
await token.connect((0, utils_2.getDummySigner)());
const preTxState = {
statesHashRoot: protocolState.hashRoot,
txoStateHashes: protocolState.stateHashList,
};
const tokenCall = await token.methods.unlock(tokenUnlockArgs, preState, preTxState, guardInfo, backtraceInfo, shPreimage, prevoutsCtx, spentScripts, {
fromUTXO: (0, utils_2.getDummyUTXO)(),
verify: false,
exec: false,
});
const witnesses = [
...(0, utils_2.callToBufferList)(tokenCall),
token.lockingScript.toBuffer(),
Buffer.from(cblockToken, 'hex'),
];
revealTx.inputs[tokenInputIndex].witnesses = witnesses;
if (verify) {
const res = (0, utils_2.verifyContract)(tokenContract.utxo, revealTx, tokenInputIndex, witnesses);
if (typeof res === 'string') {
throw new Error(`unlocking token contract at input ${tokenInputIndex} failed! ${res}`);
}
return true;
}
return true;
}
exports.unlockToken = unlockToken;
async function unlockGuard(guardContract, guardInfo, guardInputIndex, newState, revealTx, receiverTokenState, changeTokenState, changeInfo, txCtx, verify) {
const { shPreimage, prevoutsCtx, spentScripts } = txCtx;
const outputArray = (0, cat_smartcontracts_1.emptyTokenArray)();
const tokenAmountArray = (0, cat_smartcontracts_1.emptyTokenAmountArray)();
const tokenOutputIndexArray = (0, scrypt_ts_1.fill)(false, cat_smartcontracts_1.MAX_TOKEN_OUTPUT);
outputArray[0] = receiverTokenState.ownerAddr;
tokenAmountArray[0] = receiverTokenState.amount;
tokenOutputIndexArray[0] = true;
if (changeTokenState) {
outputArray[1] = changeTokenState.ownerAddr;
tokenAmountArray[1] = changeTokenState.amount;
tokenOutputIndexArray[1] = true;
}
const satoshiChangeOutputIndex = changeTokenState === null ? 1 : 2;
const { cblock: transferCblock, contract: transferGuard } = (0, utils_2.getGuardsP2TR)();
await transferGuard.connect((0, utils_2.getDummySigner)());
const outpointSatoshiArray = (0, cat_smartcontracts_1.emptyTokenArray)();
if (changeInfo != null) {
outpointSatoshiArray[satoshiChangeOutputIndex] = changeInfo.satoshis;
outputArray[satoshiChangeOutputIndex] = changeInfo.script;
tokenOutputIndexArray[satoshiChangeOutputIndex] = false;
}
const transferGuardCall = await transferGuard.methods.transfer(newState.stateHashList, outputArray, tokenAmountArray, tokenOutputIndexArray, outpointSatoshiArray, (0, scrypt_ts_1.int2ByteString)(BigInt(common_1.Postage.TOKEN_POSTAGE), 8n), guardContract.state.data, guardInfo.tx, shPreimage, prevoutsCtx, spentScripts, {
fromUTXO: (0, utils_2.getDummyUTXO)(),
verify: false,
exec: false,
});
const witnesses = [
...(0, utils_2.callToBufferList)(transferGuardCall),
transferGuard.lockingScript.toBuffer(),
Buffer.from(transferCblock, 'hex'),
];
revealTx.inputs[guardInputIndex].witnesses = witnesses;
if (verify) {
const res = (0, utils_2.verifyContract)(guardContract.utxo, revealTx, guardInputIndex, witnesses);
if (typeof res === 'string') {
throw new Error(`unlocking guard contract failed! ${res}`);
}
return true;
}
return true;
}
exports.unlockGuard = unlockGuard;
//# sourceMappingURL=functions.js.map