dappbundle/tonwallet/tonCrypto.js

205 lines
5.7 KiB
JavaScript

(function (EXPORTS) {
"use strict";
const tonCrypto = EXPORTS;
const nacl = window.nacl;
const TonWeb = window.TonWeb;
// Helpers
const generateNewID = (tonCrypto.generateNewID = function () {
var key = new Bitcoin.ECKey(false);
key.setCompressed(true);
return {
floID: key.getBitcoinAddress(),
pubKey: key.getPubKeyHex(),
privKey: key.getBitcoinWalletImportFormat(),
};
});
Object.defineProperties(tonCrypto, {
newID: {
get: () => generateNewID(),
},
hashID: {
value: (str) => {
let bytes = ripemd160(Crypto.SHA256(str, { asBytes: true }), {
asBytes: true,
});
bytes.unshift(bitjs.pub);
var hash = Crypto.SHA256(
Crypto.SHA256(bytes, {
asBytes: true,
}),
{
asBytes: true,
}
);
var checksum = hash.slice(0, 4);
return bitjs.Base58.encode(bytes.concat(checksum));
},
},
tmpID: {
get: () => {
let bytes = Crypto.util.randomBytes(20);
bytes.unshift(bitjs.pub);
var hash = Crypto.SHA256(
Crypto.SHA256(bytes, {
asBytes: true,
}),
{
asBytes: true,
}
);
var checksum = hash.slice(0, 4);
return bitjs.Base58.encode(bytes.concat(checksum));
},
},
});
function hexToBytes(hex) {
if (hex.startsWith("0x")) hex = hex.slice(2);
return new Uint8Array(hex.match(/.{1,2}/g).map((b) => parseInt(b, 16)));
}
function bytesToHex(bytes) {
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
}
function sha256Hex(hexString) {
return Crypto.SHA256(Crypto.util.hexToBytes(hexString));
}
// ---- Multi-chain (FLO, BTC, TON) ----
tonCrypto.generateMultiChain = async function (inputWif) {
const origBitjsPub = bitjs.pub;
const origBitjsPriv = bitjs.priv;
const origBitjsCompressed = bitjs.compressed;
const origCoinJsCompressed = coinjs.compressed;
bitjs.compressed = true;
coinjs.compressed = true;
const versions = {
BTC: { pub: 0x00, priv: 0x80 },
FLO: { pub: 0x23, priv: 0xa3 },
};
let privKeyHex;
let compressed = true;
if (typeof inputWif === "string" && inputWif.length > 0) {
const hexOnly = /^[0-9a-fA-F]+$/.test(inputWif.trim());
if (hexOnly && (inputWif.length === 64 || inputWif.length === 128)) {
// Raw hex private key input
if (inputWif.length === 128) {
privKeyHex = inputWif.substring(0, 64);
} else {
privKeyHex = inputWif;
}
compressed = true;
} else {
// WIF format input
try {
const decode = Bitcoin.Base58.decode(inputWif);
const keyWithVersion = decode.slice(0, decode.length - 4);
let key = keyWithVersion.slice(1);
if (key.length >= 33 && key[key.length - 1] === 0x01) {
key = key.slice(0, key.length - 1);
compressed = true;
} else {
compressed = false;
}
privKeyHex = Crypto.util.bytesToHex(key);
} catch (e) {
console.warn("Invalid WIF format, treating as seed:", e);
}
}
} else {
const newKey = generateNewID();
const decode = Bitcoin.Base58.decode(newKey.privKey);
const keyWithVersion = decode.slice(0, decode.length - 4);
let key = keyWithVersion.slice(1);
if (key.length >= 33 && key[key.length - 1] === 0x01) {
key = key.slice(0, key.length - 1);
}
privKeyHex = Crypto.util.bytesToHex(key);
}
bitjs.compressed = compressed;
coinjs.compressed = compressed;
// Generate public key
const pubKey = bitjs.newPubkey(privKeyHex);
const result = {
BTC: { address: "", privateKey: "" },
FLO: { address: "", privateKey: "" },
};
// For BTC
bitjs.pub = versions.BTC.pub;
bitjs.priv = versions.BTC.priv;
result.BTC.address = coinjs.bech32Address(pubKey).address;
result.BTC.privateKey = bitjs.privkey2wif(privKeyHex);
// For FLO
bitjs.pub = versions.FLO.pub;
bitjs.priv = versions.FLO.priv;
result.FLO.address = bitjs.pubkey2address(pubKey);
result.FLO.privateKey = bitjs.privkey2wif(privKeyHex);
bitjs.pub = origBitjsPub;
bitjs.priv = origBitjsPriv;
bitjs.compressed = origBitjsCompressed;
coinjs.compressed = origCoinJsCompressed;
// For TON
let tonSeed;
if (privKeyHex.length === 64) {
tonSeed = Crypto.util.hexToBytes(privKeyHex);
} else {
const padded = privKeyHex.padEnd(64, "0").substring(0, 64);
tonSeed = Crypto.util.hexToBytes(padded);
}
const kp = nacl.sign.keyPair.fromSeed(new Uint8Array(tonSeed));
let tonAddr;
try {
let WalletClass = null;
let tonweb = null;
tonweb = new TonWeb();
if (TonWeb.Wallets.all.v4R2) {
WalletClass = TonWeb.Wallets.all.v4R2;
console.log("Using TonWeb.Wallets.all.v4R2");
}
if (WalletClass && tonweb) {
const wallet = new WalletClass(tonweb.provider, {
publicKey: kp.publicKey,
});
const realAddr = await wallet.getAddress();
tonAddr = realAddr.toString(true, true, false);
}
} catch (e) {
console.warn("TonWeb error, using fallback:", e);
}
result.TON = {
address: tonAddr,
privateKey: bytesToHex(kp.secretKey),
};
return result;
};
// ---- Recover ----
tonCrypto.recoverFromInput = async function (input) {
const trimmed = input.trim();
return await tonCrypto.generateMultiChain(trimmed);
};
})("object" === typeof module ? module.exports : (window.tonCrypto = {}));