@ledgerhq/hw-app-eth
Version:
Ledger Hardware Wallet Ethereum Application API
187 lines (168 loc) • 15 kB
text/typescript
import nock from "nock";
import { BigNumber } from "@ethersproject/bignumber";
import { AddressZero } from "@ethersproject/constants";
import { type Transaction } from "@ethersproject/transactions";
import SignatureCALEth from "../fixtures/SignatureCALEth";
import {
UNISWAP_EXECUTE_SELECTOR,
UNISWAP_UNIVERSAL_ROUTER_ADDRESS,
} from "../../src/modules/Uniswap/constants";
import {
getCommandsAndTokensFromUniswapCalldata,
isSupported,
loadInfosForUniswap,
} from "../../src/modules/Uniswap";
nock.disableNetConnect();
jest.mock("@ledgerhq/cryptoassets-evm-signatures/data/evm/index", () => ({
get signatures() {
return {
1: SignatureCALEth,
};
},
}));
describe("Uniswap", () => {
describe("index", () => {
describe("isSupported", () => {
it("should return false for a non-Uniswap transaction", () => {
expect(isSupported("0x", AddressZero, 1, [])).toBe(false);
});
it("should return false for a Uniswap transaction with an invalid selector", () => {
expect(isSupported("0x123456", UNISWAP_UNIVERSAL_ROUTER_ADDRESS, 1, [])).toBe(false);
});
it("should return false for a Uniswap transaction with no commands", () => {
expect(isSupported(UNISWAP_EXECUTE_SELECTOR, UNISWAP_UNIVERSAL_ROUTER_ADDRESS, 1, [])).toBe(
false,
);
});
it("should return false for a transaction of multiple hops with different pool versions", () => {
expect(
isSupported(UNISWAP_EXECUTE_SELECTOR, UNISWAP_UNIVERSAL_ROUTER_ADDRESS, 1, [
["V2_SWAP_EXACT_IN", ["0x1", "0x2"]],
["V3_SWAP_EXACT_IN", ["0x2", "0x3"]],
]),
).toBe(false);
});
it("should return false for a transaction of multiple hops with non chained assets", () => {
expect(
isSupported(UNISWAP_EXECUTE_SELECTOR, UNISWAP_UNIVERSAL_ROUTER_ADDRESS, 1, [
["V2_SWAP_EXACT_IN", ["0x0A", "0x0B"]],
["V2_SWAP_EXACT_IN", ["0x0A", "0x0C"]],
]),
).toBe(false);
});
it("should return false for a transaction of multiple hops with non chained assets", () => {
expect(
isSupported(UNISWAP_EXECUTE_SELECTOR, UNISWAP_UNIVERSAL_ROUTER_ADDRESS, 1, [
["V2_SWAP_EXACT_IN", ["0x0A", "0x0B"]],
["V2_SWAP_EXACT_IN", ["0x0A", "0x0C"]],
]),
).toBe(false);
});
it("should return true for a valid Uniswap transaction", () => {
expect(
isSupported(UNISWAP_EXECUTE_SELECTOR, UNISWAP_UNIVERSAL_ROUTER_ADDRESS, 1, [
["V2_SWAP_EXACT_IN", ["0x0A", "0x0B"]],
["V2_SWAP_EXACT_IN", ["0x0B", "0x0C"]],
]),
).toBe(true);
expect(
isSupported(UNISWAP_EXECUTE_SELECTOR, UNISWAP_UNIVERSAL_ROUTER_ADDRESS, 1, [
["V3_SWAP_EXACT_IN", ["0x0A", "0x0B"]],
["V3_SWAP_EXACT_IN", ["0x0B", "0x0C"]],
]),
).toBe(true);
expect(
isSupported(UNISWAP_EXECUTE_SELECTOR, UNISWAP_UNIVERSAL_ROUTER_ADDRESS, 1, [
["V3_SWAP_EXACT_IN", ["0x0A", "0x0B"]],
["V3_SWAP_EXACT_OUT", ["0x0B", "0x0C"]],
]),
).toBe(true);
expect(
isSupported(UNISWAP_EXECUTE_SELECTOR, UNISWAP_UNIVERSAL_ROUTER_ADDRESS, 1, [
["V2_SWAP_EXACT_IN", ["0x0A", "0x0B"]],
["V2_SWAP_EXACT_OUT", ["0x0B", "0x0C"]],
]),
).toBe(true);
});
});
describe("getCommandsAndTokensFromUniswapCalldata", () => {
it("should return the commands and tokens from a Uniswap calldata", () => {
// see tx 0xc4df7ccc0527541d0e80856a8f38deedc48c84825e9355469ba02d873502ce2f
const calldata =
"0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000669b9ec100000000000000000000000000000000000000000000000000000000000000030a010c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000016000000000000000000000000055747be9f9f5beb232ad59fe7af013b81d95fd5e000000000000000000000000ffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000066c32b0d0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b00000000000000000000000000000000000000000000000000000000669b9ec100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000410d756f55acf289e9754faf91bba0a704b5c7c0aa4b1dfd551115ccbe4c7f290234e1a14265e1da0bc872a23627d997fe37a689c290d519f7b8c9bdde1b79108e1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000030ba49cbff5a00000000000000000000000000000000000000000000000089677c957272141800000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc200271055747be9f9f5beb232ad59fe7af013b81d95fd5e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000030ba49cbff5a000";
expect(getCommandsAndTokensFromUniswapCalldata(calldata, 1)).toEqual([
["PERMIT2_PERMIT", []],
[
"V3_SWAP_EXACT_OUT",
[
"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"0x55747be9f9f5beb232ad59fe7af013b81d95fd5e",
],
],
["UNWRAP_ETH", ["0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"]],
]);
});
it("should return an undefined command for unsupported commands by the Uniswap plugin", () => {
// see tx 0xc0668eb799ba8b73a396529a183b67e7905c0f70143680915ee5802b3036257b
const calldata =
"0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000669b9f4700000000000000000000000000000000000000000000000000000000000000030b000500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000004fdf8403a58c8000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000ff436c9cd1052265be510e00a661f432c539080000000000000000000000000000000000000000000000000004fdf8403a58c80000000000000000000000000000000000000000000ac736c3566d2e76b2c5978300000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc20027103ffeea07a27fab7ad1df5297fa75e77a43cb5790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006f0279540eed3fb666e8aaa4571a47e7478b6e9d000000000000000000000000000000000000000000000000000ce8a624ca9800";
expect(getCommandsAndTokensFromUniswapCalldata(calldata, 1)).toEqual([
["WRAP_ETH", ["0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"]],
[
"V3_SWAP_EXACT_IN",
[
"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"0x3ffeea07a27fab7ad1df5297fa75e77a43cb5790",
],
],
[undefined, []],
]);
});
it("should return an empty array for an invalid selector", () => {
const calldata =
"0x123456000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000669b9ec100000000000000000000000000000000000000000000000000000000000000030a010c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000016000000000000000000000000055747be9f9f5beb232ad59fe7af013b81d95fd5e000000000000000000000000ffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000066c32b0d0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b00000000000000000000000000000000000000000000000000000000669b9ec100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000410d756f55acf289e9754faf91bba0a704b5c7c0aa4b1dfd551115ccbe4c7f290234e1a14265e1da0bc872a23627d997fe37a689c290d519f7b8c9bdde1b79108e1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000030ba49cbff5a00000000000000000000000000000000000000000000000089677c957272141800000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002bc02aaa39b223fe8d0a0e5c4f27ead9083c756cc200271055747be9f9f5beb232ad59fe7af013b81d95fd5e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000030ba49cbff5a000";
expect(getCommandsAndTokensFromUniswapCalldata(calldata, 1)).toEqual([]);
});
});
describe("loadInfosForUniswap", () => {
it("should return ERC20 & plugin descriptors for a valid Uniswap transaction", async () => {
// see tx 0x88a065f47c82545b0620378d6cb2231713464dd58c0a2b8d6e9485740807b573
const calldata =
"0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000669bad2800000000000000000000000000000000000000000000000000000000000000040a08060c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000bdaa645097ef80f9d475f341d0d107a45b3a000000000000000000000000ffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000687ce06c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ef1c6e67703c7bd7107eed8303fbe6ec2554bf6b00000000000000000000000000000000000000000000000000000000687ce06c00000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000004142794d71565541a435d1bc910db84510c4d31ad35ff8c046222b2c232fcd99a6256a8f1abe7d42a44f3d92d65f9232db76df2fbef3dfa76eca64b034ed4df5321c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000001bffcca36d953d12266cb000000000000000000000000000000000000000000000000027c3dea3f90dafd00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000bdaa645097ef80f9d475f341d0d107a45b3a000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006eb940753b4b52fbec8d33c418133fdb0d4405e6000000000000000000000000000000000000000000000000000000000000003800000000000000000000000000000000000000000000000000000000000000400000000000000000000000001dc813a4ee4410f144ae2122051c1cfc436f24a00000000000000000000000000000000000000000000000000275e122c92b911e";
const transaction: Transaction = {
to: UNISWAP_UNIVERSAL_ROUTER_ADDRESS,
data: calldata,
gasLimit: BigNumber.from(21_000),
nonce: 0,
value: BigNumber.from(0),
chainId: 1,
};
const res = await loadInfosForUniswap(transaction, 1);
expect(res).toEqual({
pluginData: Buffer.from(
"07556e69737761703fc91a3afd70395cd496c647d5a6cc9d4b2b7fad3593564c3044022014391e8f355867a57fe88f6a5a4dbcb8bf8f888a9db3ff3449caf72d120396bd02200c13d9c3f79400fe0aa0434ac54d59b79503c9964a4abc3e8cd22763e0242935",
"hex",
),
tokenDescriptors: [
Buffer.from(
"0457455448c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000012000000013045022100b47ee8551c15a2cf681c649651e987d7e527c481d27c38da1f971a8242792bd3022069c3f688ac5493a23dab5798e3c9b07484765069e1d4be14321aae4d92cb8cbe",
"hex",
),
],
});
});
it("should return an empty object for an unsupported Uniswap transaction", async () => {
const transaction: Transaction = {
to: AddressZero,
data: "0x",
gasLimit: BigNumber.from(21_000),
nonce: 0,
value: BigNumber.from(0),
chainId: 1,
};
const res = await loadInfosForUniswap(transaction, 1);
expect(res).toEqual({});
});
});
});
});