@ledgerhq/coin-internet_computer
Version:
Ledger Internet Computer integration
110 lines (97 loc) • 3.54 kB
text/typescript
import { Observable } from "rxjs";
import { Account, AccountBridge, DeviceId } from "@ledgerhq/types-live";
import { getAddress } from "./bridgeHelpers/addresses";
import {
UnsignedTransaction,
createUnsignedSendTransaction,
hashTransaction,
pubkeyToDer,
} from "@zondax/ledger-live-icp/utils";
import { Cbor } from "@zondax/ledger-live-icp/agent";
import { buildOptimisticOperation } from "./buildOptimisticOperation";
import { Transaction } from "../types";
import { SignerContext } from "@ledgerhq/coin-framework/signer";
import { ICPSigner } from "../types";
import { getPath } from "../common-logic";
import { log } from "@ledgerhq/logs";
import invariant from "invariant";
const signICPTransaction = async (
unsignedTxn: UnsignedTransaction,
derivationPath: string,
signerContext: SignerContext<ICPSigner>,
account: Account,
deviceId: DeviceId,
) => {
const blob = Cbor.encode({ content: unsignedTxn });
log("debug", "[signICPTransaction] blob", Buffer.from(blob).toString("hex"));
const signatures = await signerContext(deviceId, signer =>
signer.sign(derivationPath, Buffer.from(blob)),
);
invariant(signatures.signatureRS, "[ICP](signICPTransaction) Signature not found");
invariant(account.xpub, "[ICP](signICPTransaction) Account xpub is required");
return {
signature: Buffer.from(signatures.signatureRS).toString("hex"),
callBody: {
content: unsignedTxn,
sender_pubkey: pubkeyToDer(account.xpub),
sender_sig: signatures.signatureRS,
},
};
};
export const buildSignOperation =
(signerContext: SignerContext<ICPSigner>): AccountBridge<Transaction>["signOperation"] =>
({ account, transaction, deviceId }) =>
new Observable(o => {
async function main() {
log("debug", "[signOperation] icp start fn");
log("debug", "[signOperation] transaction", transaction);
const { xpub } = account;
invariant(xpub, "[ICP](signOperation) Account xpub is required");
const { derivationPath } = getAddress(account);
const { unsignedTransaction, transferRawRequest } = createUnsignedSendTransaction(
transaction,
xpub,
);
o.next({
type: "device-signature-requested",
});
let signature: string = "";
let encodedSignedCallBlob: string = "";
const res = await signICPTransaction(
unsignedTransaction,
getPath(derivationPath),
signerContext,
account,
deviceId,
);
signature = res.signature;
encodedSignedCallBlob = Buffer.from(Cbor.encode(res.callBody)).toString("hex");
invariant(signature, "[ICP](signOperation) Signature not found");
o.next({
type: "device-signature-granted",
});
const hash = hashTransaction({
from: account.freshAddress,
to: transaction.recipient,
amount: transferRawRequest.amount.e8s,
fee: transferRawRequest.fee.e8s,
memo: transferRawRequest.memo,
created_at_time: transferRawRequest.created_at_time[0]["timestamp_nanos"],
});
const operation = await buildOptimisticOperation(account, transaction, hash);
o.next({
type: "signed",
signedOperation: {
operation,
signature,
rawData: {
encodedSignedCallBlob,
},
},
});
}
main().then(
() => o.complete(),
e => o.error(e),
);
});