PSBT.fromTransferPsbt constructor
PSBT.fromTransferPsbt(
- BtcTransferInfo btcInfo, {
- WalletType walletType = WalletType.SingleSignatureWallet,
Implementation
factory PSBT.fromTransferPsbt(BtcTransferInfo btcInfo,
{WalletType walletType = WalletType.SingleSignatureWallet}) {
String xpubkey = btcInfo.xpubkey;
final chainConf = getChainConfig(btcInfo.chain).mainnet;
final hdWallet =
bitcoin.HDWallet.fromBase58(xpubkey, network: chainConf.networkType);
final Origin btcTxData = btcInfo.origin;
int offset = 0;
Uint8List psbtBytes = psbtToBytes(btcTxData.hex);
offset += 5;
Map<String, dynamic> psbtData = {"global": {}, "inputs": [], "outputs": []};
// Global
Map<String, String> globalMap = {};
while (true) {
int keyLen = Varints.read(psbtBytes, offset);
offset += _getOffset(psbtBytes[offset]);
if (keyLen == 0) {
break;
}
Uint8List key = psbtBytes.sublist(offset, offset + keyLen);
offset += keyLen;
int valueLen = Varints.read(psbtBytes, offset);
offset += _getOffset(psbtBytes[offset]);
Uint8List value = psbtBytes.sublist(offset, offset + valueLen);
offset += valueLen;
globalMap[Converter.bytesToHex(key)] = Converter.bytesToHex(value);
}
psbtData["global"] = globalMap;
// Inputs
if (psbtData["global"]["00"] == null) {
throw Exception('Invalid PSBT');
}
Transaction globalTx =
Transaction.parsePsbtTransaction(psbtData["global"]["00"]);
for (int i = 0; i < globalTx.inputs.length; i++) {
Map<String, String> inputData = {};
while (true) {
int keyLen = Varints.read(psbtBytes, offset);
offset += _getOffset(psbtBytes[offset]);
if (keyLen == 0) {
break;
}
Uint8List key = psbtBytes.sublist(offset, offset + keyLen);
offset += keyLen;
int valueLen = Varints.read(psbtBytes, offset);
offset += _getOffset(psbtBytes[offset]);
Uint8List value = psbtBytes.sublist(offset, offset + valueLen);
offset += valueLen;
inputData[Converter.bytesToHex(key)] = Converter.bytesToHex(value);
}
//derivation path
String bip32DerivationKeyType =
getKeyType(inputKeyType, 'BIP32_DERIVATION');
String derivationPath =
btcTxData.inputs[i].path.derivation ?? btcInfo.path;
if (walletType == WalletType.SingleSignatureWallet) {
final List<int> pathList = getHDPath(derivationPath);
final child = hdWallet.derive(pathList[3]).derive(pathList[4]);
String publicKey = child.pubKey!;
String toxonlypub = child.pubKey!.length == 64
? child.pubKey!
: child.pubKey!.substring(2);
Uint8List fingerPrint = hdWallet.fingerprint!;
if (btcInfo.txType != fx.BtcTxType.TAPROOT) {
inputData[bip32DerivationKeyType + publicKey] = Converter.bytesToHex(
fingerPrint) +
Converter.bytesToHex(_serializeDerivationPath(derivationPath));
} else {
inputData[PSBT_IN_TAP_BIP32_DERIVATION + toxonlypub] =
'00${Converter.bytesToHex(fingerPrint)}${Converter.bytesToHex(_serializeDerivationPath(derivationPath))}';
}
}
psbtData["inputs"].add(inputData);
}
// Change
String receiveAddress = btcInfo.outputAddress;
final outputs = btcTxData.outputs;
final result = outputs.firstWhere((e) => e.address == receiveAddress);
num actualAmount =
result.amount ?? NumberUtil.toDouble(result.value) / 1000000;
List<int> outputsIndex = List.generate(outputs.length, (index) => index);
String inputAddress = btcInfo.inputAddress;
for (var i = 0; i < outputs.length; i++) {
if (outputs[i].address == receiveAddress) {
if (outputs[i].amount == actualAmount) {
outputsIndex.remove(i);
}
}
}
int changeIndex = -1; // total - payoff;
if (outputsIndex.isNotEmpty &&
(outputs[outputsIndex[0]].address == inputAddress ||
(btcInfo.chain.toLowerCase() == 'bch' &&
(outputs[outputsIndex[0]].address ==
bitcoin.Address.bchToLegacy(inputAddress) ||
outputs[outputsIndex[0]].address ==
bitcoin.Address.legacyToBch(
address: inputAddress, prefix: 'bitcoincash'))))) {
changeIndex = outputsIndex[0];
}
// Outputs
for (int i = 0; i < globalTx.outputs.length; i++) {
Map<String, String> outputData = {};
while (true) {
int keyLen = Varints.read(psbtBytes, offset);
offset += _getOffset(psbtBytes[offset]);
if (keyLen == 0) {
break;
}
Uint8List key = psbtBytes.sublist(offset, offset + keyLen);
offset += keyLen;
int valueLen = Varints.read(psbtBytes, offset);
offset += _getOffset(psbtBytes[offset]);
Uint8List value = psbtBytes.sublist(offset, offset + valueLen);
offset += valueLen;
outputData[Converter.bytesToHex(key)] = Converter.bytesToHex(value);
}
if (i == changeIndex) {
String bip32DerivationKeyType =
getKeyType(outputKeyType, 'BIP32_DERIVATION');
String derivationPath = btcTxData.outputs[i].path ?? btcInfo.path;
final List<int> pathList = getHDPath(derivationPath);
final child = hdWallet.derive(pathList[3]).derive(pathList[4]);
String publicKey = child.pubKey!;
Uint8List fingerPrint = hdWallet.fingerprint!;
outputData[bip32DerivationKeyType + publicKey] =
Converter.bytesToHex(fingerPrint) +
Converter.bytesToHex(_serializeDerivationPath(derivationPath));
}
psbtData["outputs"].add(outputData);
}
return PSBT(psbtData);
}