@bandprotocol/bandchain.js
Version:
Library for interacting with BandChain in browser and Node.js environments
311 lines (310 loc) • 12.7 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Address = exports.PublicKey = exports.PrivateKey = exports.Ledger = void 0;
const bip39 = __importStar(require("bip39"));
const bip32_1 = require("bip32");
const bech32_1 = require("bech32");
const secp256k1_1 = __importStar(require("secp256k1"));
const crypto_1 = __importDefault(require("crypto"));
const ecpair_1 = __importDefault(require("ecpair"));
const ecc = __importStar(require("@bitcoinerlab/secp256k1"));
const ledger_cosmos_js_1 = __importDefault(require("ledger-cosmos-js"));
const helpers_1 = require("./helpers");
const error_1 = require("./error");
const keys_pb_1 = require("../codegen/cosmos/crypto/secp256k1/keys_pb");
const ECPair = (0, ecpair_1.default)(ecc);
const bip32 = (0, bip32_1.BIP32Factory)(ecc);
const BECH32_PUBKEY_ACC_PREFIX = 'bandpub';
const BECH32_PUBKEY_VAL_PREFIX = 'bandvaloperpub';
const BECH32_PUBKEY_CONS_PREFIX = 'bandvalconspub';
const BECH32_ADDR_ACC_PREFIX = 'band';
const BECH32_ADDR_VAL_PREFIX = 'bandvaloper';
const BECH32_ADDR_CONS_PREFIX = 'bandvalcons';
const DEFAULT_DERIVATION_PATH = "m/44'/494'/0'/0/0";
const DEFAULT_DERIVATION_PATH_LEDGER = "m/44'/118'/0'/0/0";
var ConnectType;
(function (ConnectType) {
ConnectType[ConnectType["Node"] = 0] = "Node";
ConnectType[ConnectType["Web"] = 1] = "Web";
})(ConnectType || (ConnectType = {}));
class Ledger {
constructor() {
this.hidPath = DEFAULT_DERIVATION_PATH_LEDGER;
this.disconnect = () => new Promise((resolve) => resolve());
}
static connectLedgerWeb(hidPath = DEFAULT_DERIVATION_PATH_LEDGER) {
return __awaiter(this, void 0, void 0, function* () {
return Ledger.connect(hidPath, ConnectType.Web);
});
}
static connectLedgerNode(hidPath = DEFAULT_DERIVATION_PATH_LEDGER) {
return __awaiter(this, void 0, void 0, function* () {
return Ledger.connect(hidPath, ConnectType.Node);
});
}
static connect(hidPath, connectType) {
return __awaiter(this, void 0, void 0, function* () {
if (!(0, helpers_1.isBip44)(hidPath))
throw Error('Not BIP 44');
let ledger = new Ledger();
ledger.hidPath = hidPath;
yield ledger._connect(connectType);
return ledger;
});
}
_connect(connectType) {
return __awaiter(this, void 0, void 0, function* () {
if (this.cosmosApp)
return;
let transport;
switch (connectType) {
case ConnectType.Node:
const { default: TransportNodeHid, } = require('@ledgerhq/hw-transport-node-hid');
transport = yield TransportNodeHid.create(3);
break;
case ConnectType.Web:
if (navigator.usb) {
const { default: TransportWebUSB, } = require('@ledgerhq/hw-transport-webusb');
transport = yield TransportWebUSB.create(3);
}
else {
const { default: TransportWebHid, } = require('@ledgerhq/hw-transport-webhid');
transport = yield TransportWebHid.create(3);
}
break;
}
const ledgerCosmosApp = new ledger_cosmos_js_1.default(transport);
this.cosmosApp = ledgerCosmosApp;
this.disconnect = () => transport.close();
yield this.isCosmosAppOpen();
});
}
checkLedgerError(response, errorOnUndefined) {
if (!response) {
if (errorOnUndefined)
throw new Error(errorOnUndefined);
return;
}
switch (response.error_message) {
case `No errors`:
return;
default:
throw new Error(response.error_message);
}
}
isCosmosAppOpen() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield (0, helpers_1.promiseTimeout)(this.cosmosApp.appInfo(), 5000);
this.checkLedgerError(response, `Can't connect with CosmosApp`);
const { appName } = response;
if (appName.toLowerCase() !== 'cosmos')
throw new Error(`Please close ${appName} and open the Cosmos app.`);
return true;
});
}
appInfo() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield (0, helpers_1.promiseTimeout)(this.cosmosApp.appInfo(), 5000);
this.checkLedgerError(response, `Can't connect with CosmosApp`);
return response;
});
}
sign(transaction) {
return __awaiter(this, void 0, void 0, function* () {
const response = yield this.cosmosApp.sign((0, helpers_1.bip44ToArray)(this.hidPath), transaction.getSignMessage().toString());
this.checkLedgerError(response);
return Buffer.from((0, secp256k1_1.signatureImport)(response.signature));
});
}
getPubKeyAndBech32Address() {
return __awaiter(this, void 0, void 0, function* () {
const response = yield (0, helpers_1.promiseTimeout)(this.cosmosApp.getAddressAndPubKey((0, helpers_1.bip44ToArray)(this.hidPath), 'band'), 5000);
this.checkLedgerError(response, `Can't connect with CosmosApp`);
return {
bech32_address: response.bech32_address,
pubKey: PublicKey.fromHex(response.compressed_pk.toString('hex')),
};
});
}
}
exports.Ledger = Ledger;
class PrivateKey {
constructor(signingKey) {
this.signingKey = signingKey;
}
static generate(path = DEFAULT_DERIVATION_PATH) {
const phrase = bip39.generateMnemonic(256);
return [phrase, this.fromMnemonic(phrase, path)];
}
static fromMnemonic(words, path = DEFAULT_DERIVATION_PATH) {
const seed = bip39.mnemonicToSeedSync(words);
const node = bip32.fromSeed(seed);
const child = node.derivePath(path);
if (!child.privateKey)
throw new error_1.CreateError('Cannot create private key');
const ecpair = ECPair.fromPrivateKey(child.privateKey, {
compressed: false,
});
if (!ecpair.privateKey)
throw new error_1.CreateError('Cannot create private key');
return new PrivateKey(ecpair.privateKey);
}
static fromHex(priv) {
return new PrivateKey(Buffer.from(priv, 'hex'));
}
toHex() {
return this.signingKey.toString('hex');
}
toPubkey() {
const pubKeyByte = secp256k1_1.default.publicKeyCreate(this.signingKey);
return PublicKey.fromHex(Buffer.from(pubKeyByte).toString('hex'));
}
sign(msg) {
const hash = crypto_1.default.createHash('sha256').update(msg).digest('hex');
const buf = Buffer.from(hash, 'hex');
const { signature } = secp256k1_1.default.ecdsaSign(buf, this.signingKey);
return Buffer.from(signature);
}
}
exports.PrivateKey = PrivateKey;
class PublicKey {
constructor(verifyKey) {
this.verifyKey = verifyKey;
}
static fromBech32(bech, _prefix) {
const { prefix, words } = bech32_1.bech32.decode(bech);
if (prefix != _prefix)
throw new error_1.ValueError('Invalid bech32 prefix');
if (words.length === 0)
throw new error_1.DecodeError('Cannot decode bech32');
return new PublicKey(Buffer.from(bech32_1.bech32.fromWords(words).slice(5)));
}
static fromHex(pub) {
return new PublicKey(Buffer.from(pub, 'hex'));
}
static fromAccBech32(bech) {
return this.fromBech32(bech, BECH32_PUBKEY_ACC_PREFIX);
}
static fromValBech32(bech) {
return this.fromBech32(bech, BECH32_PUBKEY_VAL_PREFIX);
}
static fromConsBech32(bech) {
return this.fromBech32(bech, BECH32_PUBKEY_CONS_PREFIX);
}
toBech32(prefix) {
const hex = Buffer.concat([
Buffer.from('eb5ae98721', 'hex'),
this.verifyKey,
]);
const words = bech32_1.bech32.toWords(Buffer.from(hex));
if (words.length === 0)
throw new error_1.UnsuccessfulCallError('Unsuccessful bech32.toWords call');
return bech32_1.bech32.encode(prefix, words);
}
toPubkeyProto() {
const publicKeyProto = new keys_pb_1.PubKey();
publicKeyProto.setKey(this.verifyKey);
return publicKeyProto;
}
toAccBech32() {
return this.toBech32(BECH32_PUBKEY_ACC_PREFIX);
}
toValBech32() {
return this.toBech32(BECH32_PUBKEY_VAL_PREFIX);
}
toConsBech32() {
return this.toBech32(BECH32_PUBKEY_CONS_PREFIX);
}
toHex() {
return this.verifyKey.toString('hex');
}
toAddress() {
const hash = crypto_1.default.createHash('sha256').update(this.verifyKey).digest();
return Address.fromHex(crypto_1.default.createHash('ripemd160').update(hash).digest('hex'));
}
verify(msg, sig) {
const hash = crypto_1.default.createHash('sha256').update(msg).digest('hex');
const buf = Buffer.from(hash, 'hex');
return secp256k1_1.default.ecdsaVerify(sig, buf, this.verifyKey);
}
}
exports.PublicKey = PublicKey;
class Address {
constructor(addr) {
this.addr = addr;
}
static fromBech32(bech, _prefix) {
const { prefix, words } = bech32_1.bech32.decode(bech);
if (prefix != _prefix)
throw new error_1.ValueError('Invalid bech32 prefix');
if (words.length === 0)
throw new error_1.DecodeError('Cannot decode bech32');
return new Address(Buffer.from(bech32_1.bech32.fromWords(words)));
}
static fromHex(hex) {
return new Address(Buffer.from(hex, 'hex'));
}
static fromAccBech32(bech) {
return this.fromBech32(bech, BECH32_ADDR_ACC_PREFIX);
}
static fromValBech32(bech) {
return this.fromBech32(bech, BECH32_ADDR_VAL_PREFIX);
}
static fromConsBech32(bech) {
return this.fromBech32(bech, BECH32_ADDR_CONS_PREFIX);
}
toBech32(prefix) {
const words = bech32_1.bech32.toWords(this.addr);
if (words.length === 0)
throw new error_1.UnsuccessfulCallError('Unsuccessful bech32.toWords call');
return bech32_1.bech32.encode(prefix, words);
}
toAccBech32() {
return this.toBech32(BECH32_ADDR_ACC_PREFIX);
}
toValBech32() {
return this.toBech32(BECH32_ADDR_VAL_PREFIX);
}
toConsBech32() {
return this.toBech32(BECH32_ADDR_CONS_PREFIX);
}
toHex() {
return this.addr.toString('hex');
}
}
exports.Address = Address;