@ledgerhq/hw-app-algorand
Version:
Ledger Hardware Wallet Algorand Application API
106 lines • 3.8 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const bip32_path_1 = __importDefault(require("bip32-path"));
const errors_1 = require("@ledgerhq/errors");
const utils_1 = require("./utils");
const CHUNK_SIZE = 250;
// const P1_FIRST = 0x00;
const P1_MORE = 0x80;
const P1_WITH_ACCOUNT_ID = 0x01;
const P2_LAST = 0x00;
const P2_MORE = 0x80;
const SW_OK = 0x9000;
const SW_CANCEL = 0x6986;
const P1_WITH_REQUEST_USER_APPROVAL = 0x80;
// algo spec
const CLA = 0x80;
const INS_GET_PUBLIC_KEY = 0x03;
const INS_SIGN_MSGPACK = 0x08;
/**
* Algorand API
*
* @example
* import Algorand from "@ledgerhq/hw-app-algorand";
* const algo = new Algorand(transport)
*/
class Algorand {
transport;
constructor(transport) {
this.transport = transport;
transport.decorateAppAPIMethods(this, ["getAddress", "sign"], "ALGO");
}
/**
* get Algorant address for a given BIP 32 path.
* @param path a path in BIP 32 format
* @option boolDisplay optionally enable or not the display
* @return an object with a publicKey, address and (optionally) chainCode
* @example
* cosmos.getAddress("44'/283'/0'/0/0").then(o => o.address)
*/
getAddress(path, boolDisplay) {
const bipPath = bip32_path_1.default.fromString(path).toPathArray();
const buf = Buffer.alloc(4);
buf.writeUInt32BE(bipPath[2], 0);
return this.transport
.send(CLA, INS_GET_PUBLIC_KEY, boolDisplay ? P1_WITH_REQUEST_USER_APPROVAL : 0, 0, buf, [
SW_OK,
])
.then(response => {
const buffer = Buffer.from(response.slice(0, 32));
const publicKey = buffer.toString("hex");
const address = (0, utils_1.encodeAddress)(buffer);
return {
publicKey,
address,
};
});
}
foreach(arr, callback) {
function iterate(index, array, result) {
if (index >= array.length) {
return result;
}
else
return callback(array[index], index).then(function (res) {
result.push(res);
return iterate(index + 1, array, result);
});
}
return Promise.resolve().then(() => iterate(0, arr, []));
}
async sign(path, message) {
const bipPath = bip32_path_1.default.fromString(path).toPathArray();
const buf = Buffer.alloc(4);
buf.writeUInt32BE(bipPath[2], 0);
const chunks = [];
const buffer = Buffer.concat([buf, Buffer.from(message, "hex")]);
for (let i = 0; i < buffer.length; i += CHUNK_SIZE) {
let end = i + CHUNK_SIZE;
if (i > buffer.length) {
end = buffer.length;
}
chunks.push(buffer.slice(i, end));
}
let response = {};
return this.foreach(chunks, (data, j) => this.transport
.send(CLA, INS_SIGN_MSGPACK, j === 0 ? P1_WITH_ACCOUNT_ID : P1_MORE, j + 1 === chunks.length ? P2_LAST : P2_MORE, data, [SW_OK, SW_CANCEL])
.then(apduResponse => (response = apduResponse))).then(() => {
const errorCodeData = response;
if (errorCodeData === 0x6986) {
throw new errors_1.UserRefusedOnDevice();
}
let signature = null;
if (response.length > 0) {
signature = response.slice(0, response.length);
}
return {
signature: signature,
};
});
}
}
exports.default = Algorand;
//# sourceMappingURL=Algorand.js.map