@ledgerhq/coin-celo
Version:
70 lines • 3.04 kB
JavaScript
import { BigNumber } from "bignumber.js";
import { Observable } from "rxjs";
import { FeeNotLoaded } from "@ledgerhq/errors";
import { encodeTransaction, recoverTransaction } from "@celo/wallet-base";
import { buildOptimisticOperation } from "./buildOptimisticOperation";
import buildTransaction from "./buildTransaction";
import { determineFees } from "../network/sdk";
/**
* Sign Transaction with Ledger hardware
*/
export const buildSignOperation = (signerContext) => ({ account, transaction, deviceId, }) => new Observable(o => {
let cancelled;
async function main() {
const { fees } = transaction;
if (!fees)
throw new FeeNotLoaded();
const unsignedTransaction = await buildTransaction(account, transaction);
const { chainId, to } = unsignedTransaction;
await signerContext(deviceId, signer => {
return signer.verifyTokenInfo(to, chainId);
});
await determineFees(unsignedTransaction);
const rlpEncodedTransaction = await signerContext(deviceId, signer => {
return signer.rlpEncodedTxForLedger(unsignedTransaction);
});
o.next({ type: "device-signature-requested" });
const response = (await signerContext(deviceId, signer => {
return signer.signTransaction(account.freshAddressPath, trimLeading0x(rlpEncodedTransaction.rlpEncode));
}));
const { address } = await signerContext(deviceId, signer => {
return signer.getAddress(account.freshAddressPath);
});
const convertedResponse = { ...response, v: response.v.toString() };
if (cancelled)
return;
const signature = parseSigningResponse(convertedResponse, chainId);
o.next({ type: "device-signature-granted" });
const encodedTransaction = await encodeTransaction(rlpEncodedTransaction, signature);
const [_, recoveredAddress] = recoverTransaction(encodedTransaction.raw);
if (recoveredAddress !== address) {
throw new Error("celo: there was a signing error, the recovered address doesn't match the your ledger address, the operation was cancelled");
}
const operation = buildOptimisticOperation(account, transaction, transaction.fees ?? new BigNumber(0));
o.next({
type: "signed",
signedOperation: {
operation,
signature: encodedTransaction.raw,
},
});
}
main().then(() => o.complete(), e => o.error(e));
return () => {
cancelled = true;
};
});
const trimLeading0x = (input) => (input.startsWith("0x") ? input.slice(2) : input);
const parseSigningResponse = (response, chainId) => {
// EIP155
const sigV = parseInt(response.v, 16);
let eip155V = chainId * 2 + 35;
eip155V = sigV;
return {
s: Buffer.from(response.s, "hex"),
v: eip155V,
r: Buffer.from(response.r, "hex"),
};
};
export default buildSignOperation;
//# sourceMappingURL=signOperation.js.map