@ledgerhq/coin-tezos
Version:
285 lines (260 loc) • 8.3 kB
text/typescript
import BigNumber from "bignumber.js";
import { DEFAULT_FEE, OpKind } from "@taquito/taquito";
import { SignOperationEvent } from "@ledgerhq/types-live";
import buildSignOperation, { getOperationContents } from "./signOperation";
import config, { type TezosCoinConfig } from "../config";
import { TezosSigner } from "../types";
import { createFixtureAccount, createFixtureTransaction } from "../types/bridge.fixture";
import { mockConfig } from "../test/config";
const mockForgeOperations = jest.fn();
const mockEstimateReveal = jest.fn();
jest.mock("@taquito/taquito", () => ({
...jest.requireActual("@taquito/taquito"),
TezosToolkit: jest.fn().mockReturnValue({
setProvider: jest.fn(),
rpc: {
getBlock: jest.fn().mockResolvedValue({ hash: "hash" }),
getContract: jest.fn().mockResolvedValue({ counter: "12" }),
forgeOperations: () => mockForgeOperations(),
},
estimate: {
reveal: () => mockEstimateReveal(),
},
}),
}));
describe("signOperation", () => {
const mockSign = jest.fn().mockResolvedValue({
bytes: "SIG_BYTES",
sig: "SIG",
prefixSig: "PREFIX_SIG",
sbytes: "03SBYTES",
});
const fakeSigner = {
getAddress: jest.fn(),
signOperation: jest.fn(),
createLedgerSigner: () => ({
publicKey: () => Promise.resolve("PUBKEY"),
publicKeyHash: () => Promise.resolve("PUBKEYHASH"),
sign: (arg: unknown) => mockSign(arg),
secretKey: () => Promise.resolve(undefined),
}),
};
const signerContext = <T>(_deviceId: string, fn: (signer: TezosSigner) => Promise<T>) =>
fn(fakeSigner);
const signOperation = buildSignOperation(signerContext);
const deviceId = "dummyDeviceId";
beforeAll(() => {
config.setCoinConfig((): TezosCoinConfig => mockConfig as TezosCoinConfig);
});
afterEach(() => {
mockForgeOperations.mockClear();
mockEstimateReveal.mockClear();
mockSign.mockClear();
});
it("returns events in the right order", done => {
// GIVEN
const account = createFixtureAccount();
const transaction = createFixtureTransaction({ fees: BigNumber(0) });
mockForgeOperations.mockResolvedValue("FORGED_OP");
// WHEN & THEN
const expectedEvent = [
{
type: "device-signature-requested",
},
{
type: "device-signature-granted",
},
{
type: "signed",
},
];
let eventIdx = 0;
signOperation({ account, deviceId, transaction }).forEach(e => {
try {
expect(e.type).toEqual(expectedEvent[eventIdx].type);
eventIdx++;
if (eventIdx === expectedEvent.length) {
done();
}
} catch (err) {
done(err);
}
});
});
it("returns signature value from LedgerSigner", done => {
// GIVEN
const account = createFixtureAccount();
const transaction = createFixtureTransaction({ fees: new BigNumber(0) });
mockForgeOperations.mockResolvedValue("f0463d");
// WHEN & THEN
const subscriber = signOperation({ account, deviceId, transaction }).subscribe(
(e: SignOperationEvent) => {
if (e.type === "signed") {
const signature = e.signedOperation.signature;
expect(signature).toEqual("SBYTES");
expect(mockForgeOperations).toHaveBeenCalledTimes(1);
expect(mockSign).toHaveBeenCalledTimes(1);
expect(mockSign.mock.lastCall[0]).toEqual(
Buffer.concat([Buffer.from("03", "hex"), Buffer.from("f0463d", "hex")]).toString("hex"),
);
// Cleanup
subscriber.unsubscribe();
done();
}
},
);
});
});
describe("getOperationContents - revealed account", () => {
const account = createFixtureAccount({ freshAddress: "tz1addr" });
it("mode - send", async () => {
const transaction = createFixtureTransaction({
family: "tezos",
amount: new BigNumber(0),
recipient: "RECIPIENT_ADD",
fees: new BigNumber(0),
mode: "send",
gasLimit: new BigNumber(0),
storageLimit: new BigNumber(0),
});
const { type, contents } = await getOperationContents({
account,
transaction,
counter: 0,
public_key: "pk",
public_key_hash: "pkh",
});
expect(type).toBe("OUT");
expect(contents.length).toEqual(1);
expect(contents).toStrictEqual([
{
kind: OpKind.TRANSACTION,
amount: transaction.amount.toString(),
destination: transaction.recipient,
source: "tz1addr",
counter: "1",
fee: new BigNumber(0).toString(),
gas_limit: new BigNumber(0).toString(),
storage_limit: new BigNumber(0).toString(),
},
]);
expect(mockEstimateReveal).not.toHaveBeenCalled();
});
it("mode - delegate", async () => {
const transaction = createFixtureTransaction({
family: "tezos",
amount: new BigNumber(0),
recipient: "RECIPIENT_ADD",
fees: new BigNumber(0),
mode: "delegate",
gasLimit: new BigNumber(0),
storageLimit: new BigNumber(0),
});
const { type, contents } = await getOperationContents({
account,
transaction,
counter: 0,
public_key: "pk",
public_key_hash: "pkh",
});
expect(type).toBe("DELEGATE");
expect(contents.length).toEqual(1);
expect(contents).toStrictEqual([
{
kind: OpKind.DELEGATION,
source: "tz1addr",
counter: "1",
fee: new BigNumber(0).toString(),
gas_limit: new BigNumber(0).toString(),
storage_limit: new BigNumber(0).toString(),
delegate: transaction.recipient,
},
]);
expect(mockEstimateReveal).not.toHaveBeenCalled();
});
it("mode - undelegate", async () => {
const transaction = createFixtureTransaction({
family: "tezos",
amount: new BigNumber(0),
recipient: "",
fees: new BigNumber(0),
mode: "undelegate",
gasLimit: new BigNumber(0),
storageLimit: new BigNumber(0),
});
const { type, contents } = await getOperationContents({
account,
transaction,
counter: 0,
public_key: "pk",
public_key_hash: "pkh",
});
expect(type).toBe("UNDELEGATE");
expect(contents.length).toEqual(1);
expect(contents).toStrictEqual([
{
kind: OpKind.DELEGATION,
source: "tz1addr",
counter: "1",
fee: new BigNumber(0).toString(),
gas_limit: new BigNumber(0).toString(),
storage_limit: new BigNumber(0).toString(),
},
]);
expect(mockEstimateReveal).not.toHaveBeenCalled();
});
describe("getOperationContents - not revealed account", () => {
const account = createFixtureAccount({
freshAddress: "tz1addr",
// We don't use `counter` in the OperationContents computation
tezosResources: { revealed: false, counter: -10 },
});
it("mode - send", async () => {
// Given
mockEstimateReveal.mockResolvedValue({
gasLimit: 100,
storageLimit: 200,
});
const transaction = createFixtureTransaction({
family: "tezos",
amount: new BigNumber(0),
recipient: "RECIPIENT_ADD",
fees: new BigNumber(0),
mode: "send",
gasLimit: new BigNumber(0),
storageLimit: new BigNumber(0),
});
const { type, contents } = await getOperationContents({
account,
transaction,
counter: 0,
public_key: "pk",
public_key_hash: "pkh",
});
expect(type).toBe("OUT");
expect(contents.length).toEqual(2);
expect(contents).toStrictEqual([
{
kind: OpKind.REVEAL,
fee: DEFAULT_FEE.REVEAL.toString(),
gas_limit: "100",
storage_limit: "200",
source: "pkh",
counter: "1",
public_key: "pk",
},
{
kind: OpKind.TRANSACTION,
amount: transaction.amount.toString(),
destination: transaction.recipient,
source: "tz1addr",
counter: "2",
fee: new BigNumber(0).toString(),
gas_limit: new BigNumber(0).toString(),
storage_limit: new BigNumber(0).toString(),
},
]);
expect(mockEstimateReveal).toHaveBeenCalledTimes(1);
});
});
});