aion-gql-webcomponents
Version:
AION Webcomponents using AION GraphQL
97 lines (96 loc) • 3.85 kB
JavaScript
import Transport from "@ledgerhq/hw-transport-u2f";
import { Buffer } from "buffer";
import { Util } from "./Util";
import { CryptoUtil } from "../../util/CryptoUtil";
import { TransactionUtil } from "../../util/TransactionUtil";
export class LedgerProvider {
constructor() {
this.path = "44'/425'/0'/0'/0'";
this.connect();
}
async connect() {
return Transport.create().then(_transport => {
_transport.decorateAppAPIMethods(this, [
"getAddress",
"sign"
], "aion");
return _transport;
});
}
async unlock(progressCallback) {
try {
if (!this.transport)
this.transport = await this.connect();
let result = await this.getAddress(this.path, true, false);
if (progressCallback)
progressCallback(100);
this.address = result.address;
this.publicKey = result.publicKey;
return [result.address, result.publicKey];
}
catch (e) {
console.log("Error getting address", e);
throw e;
}
}
getAddress(path, boolDisplay, boolChaincode) {
let paths = Util.splitPath(path);
let buffer = new Buffer(1 + paths.length * 4);
buffer[0] = paths.length;
paths.forEach((element, index) => {
buffer.writeUInt32BE(element, 1 + 4 * index);
});
return this.transport.send(0xe0, 0x02, boolDisplay ? 0x01 : 0x00, boolChaincode ? 0x01 : 0x00, buffer)
.then(response => {
let result = {
publicKey: '',
address: ''
};
if (response.length < 64)
throw new Error("Invalid response for getAddress");
let publicKeyBuff = response.slice(0, 32);
let addressBuff = response.slice(32, 64);
result.publicKey = CryptoUtil.uia2hex(publicKeyBuff, true);
result.address = CryptoUtil.uia2hex(addressBuff);
return result;
});
}
async sign(transaction) {
let rawTransaction = TransactionUtil.rlpEncode(transaction);
let rawTxHash = CryptoUtil.uia2hex(rawTransaction, true);
let paths = Util.splitPath(this.path);
let offset = 0;
let rawTx = new Buffer(rawTxHash, "hex");
let toSend = [];
let response;
while (offset !== rawTx.length) {
let maxChunkSize = offset === 0 ? 150 - 1 - paths.length * 4 : 150;
let chunkSize = offset + maxChunkSize > rawTx.length
? rawTx.length - offset
: maxChunkSize;
let buffer = new Buffer(offset === 0 ? 1 + paths.length * 4 + chunkSize : chunkSize);
if (offset === 0) {
buffer[0] = paths.length;
paths.forEach((element, index) => {
buffer.writeUInt32BE(element, 1 + 4 * index);
});
rawTx.copy(buffer, 1 + 4 * paths.length, offset, offset + chunkSize);
}
else {
rawTx.copy(buffer, 0, offset, offset + chunkSize);
}
toSend.push(buffer);
offset += chunkSize;
}
return Util.foreach(toSend, (data, i) => {
return this.transport
.send(0xe0, 0x04, i === 0 ? 0x00 : 0x80, 0x00, data)
.then(apduResponse => {
response = apduResponse;
});
}).then(() => {
let signature = response.slice(0, 64);
return TransactionUtil.verifyAndEncodedSignTransaction(transaction, rawTransaction, signature, CryptoUtil.hex2ua(this.publicKey));
});
}
}