Skip to content
This repository has been archived by the owner on Jun 27, 2022. It is now read-only.

LL-6794 Add ability to overrides plugins + be chainId ready #658

Merged
merged 4 commits into from
Sep 16, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/hw-app-eth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ Retrieve the metadatas a given contract address and a method selector

- `contractAddress` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
- `selector` **[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
- `chainId` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)**
- `userPluginsLoadConfig` **PluginsLoadConfig**

Returns **[Promise](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)<(ContractMethod | [undefined](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/undefined))>**

Expand Down Expand Up @@ -97,6 +99,7 @@ Ethereum API

- `transport` **Transport**
- `scrambleKey` (optional, default `"w0w"`)
- `pluginsLoadConfig` **PluginsLoadConfig** (optional, default `{}`)

#### Examples

Expand Down
20 changes: 18 additions & 2 deletions packages/hw-app-eth/src/Eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { BigNumber } from "bignumber.js";
import { ethers } from "ethers";
import { byContractAddressAndChainId } from "./erc20";
import { loadInfosForContractMethod } from "./contracts";
import type { PluginsLoadConfig } from "./contracts";

export type StarkQuantizationType =
| "eth"
Expand Down Expand Up @@ -68,9 +69,19 @@ const remapTransactionRelatedErrors = (e) => {

export default class Eth {
transport: Transport;
pluginsLoadConfig: PluginsLoadConfig;

constructor(transport: Transport, scrambleKey = "w0w") {
setPluginsLoadConfig(pluginsLoadConfig: PluginsLoadConfig): void {
this.pluginsLoadConfig = pluginsLoadConfig;
}

constructor(
transport: Transport,
scrambleKey = "w0w",
pluginsLoadConfig: PluginsLoadConfig = {}
) {
this.transport = transport;
this.pluginsLoadConfig = pluginsLoadConfig;
transport.decorateAppAPIMethods(
this,
[
Expand Down Expand Up @@ -313,7 +324,12 @@ export default class Eth {

if (decodedTx.data.length >= 10) {
const selector = decodedTx.data.substring(0, 10);
const infos = await loadInfosForContractMethod(decodedTx.to, selector);
const infos = await loadInfosForContractMethod(
decodedTx.to,
selector,
chainIdTruncated,
this.pluginsLoadConfig
);

if (infos) {
const { plugin, payload, signature, erc20OfInterest, abi } = infos;
Expand Down
52 changes: 42 additions & 10 deletions packages/hw-app-eth/src/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,54 @@ type ContractMethod = {
abi: any;
};

// example of payload https://cdn.live.ledger.com/plugins/ethereum/1.json
export type PluginsLoadConfig = {
// fetch against an api (base url is an api that hosts /plugins/ethereum/${chainId}.json )
// set to null will disable it
baseURL?: string | null;
// provide manually some extra plugins to add for the resolution (e.g. for dev purpose)
// object will be merged with the returned value of the Ledger cdn payload
extraPlugins?: any | null;
};

const defaultPluginsLoadConfig = {
baseURL: "https://cdn.live.ledger.com",
extraPlugins: null,
};

/**
* Retrieve the metadatas a given contract address and a method selector
*/
export const loadInfosForContractMethod = async (
contractAddress: string,
selector: string
selector: string,
chainId: number,
userPluginsLoadConfig: PluginsLoadConfig
): Promise<ContractMethod | undefined> => {
const data = await axios
.get("https://cdn.live.ledger.com/plugins/ethereum.json")
.then((r) => r.data)
.catch((e) => {
if (e.response && 400 <= e.response.status && e.response.status < 500) {
return null; // not found cases can be ignored to allow future changes in endpoint without failing a signature to be done.
}
throw e;
});
const { baseURL, extraPlugins } = {
...defaultPluginsLoadConfig,
...userPluginsLoadConfig,
};
let data = !baseURL
? {}
: await axios
.get(`${baseURL}/plugins/ethereum/${chainId}.json`)
.then((r) => r.data)
.catch((e) => {
if (
e.response &&
400 <= e.response.status &&
e.response.status < 500
) {
return null; // not found cases can be ignored to allow future changes in endpoint without failing a signature to be done.
}
throw e;
});

if (extraPlugins) {
data = { ...data, ...extraPlugins };
}

if (!data) return;

const lcSelector = selector.toLowerCase();
Expand Down
85 changes: 62 additions & 23 deletions packages/hw-app-eth/tests/Eth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Eth from "../src/Eth";
import { TokenInfo } from "../src/erc20";
import { BigNumber } from "bignumber.js";
import { byContractAddressAndChainId } from "../src/erc20";
import { listen, log } from "@ledgerhq/logs";
import paraswapJSON from "./paraswap.json";

test("getAppConfiguration", async () => {
const transport = await openTransportReplayer(
Expand Down Expand Up @@ -125,32 +125,71 @@ test("signTransaction supports EIP2930", async () => {
});
});

const paraswapAPDUs = `=> e0120000670850617261737761701bd435f3c054b6e901b7b108a0ab7617c808677bcfc0afeb304402201c0cbe69aac517825b3a6eb5e7251e8fd57ff93a43bd3df52c7a841818eda81b022001a10cc326efaee2463fc96e7c29739c308fb8179bd2ac37303662bae4f7705c
<= 9000
=> e00a0000680531494e4348111111111117dc0aa78b770fa6a738034120c3020000001200000001304402204623e5f1375c54a446157ae8a739204284cf053634b7abd083dc5f5d2675c4e702206ff94b4c84ba9e93f44065c38d7c92506621fa69ba04f767aa58221de8afbf17
<= 9000
=> e004000096058000002c8000003c800000000000000000000000f903cd82043d8509c765240083042e73941bd435f3c054b6e901b7b108a0ab7617c808677b80b903a4cfc0afeb000000000000000000000000111111111117dc0aa78b770fa6a738034120c302000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000
<= 9000
=> e0048000960000000af10f7eb24f506cfd00000000000000000000000000000000000000000000000002a5b905b3c9fa4c00000000000000000000000000000000000000000000000002baaee8d905020a000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000
<= 9000
=> e004800096000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000000
<= 9000
=> e004800096000000000000000000000000000000000000000000000000000000000000000100000000000000000000000086d3579b043585a97532514016dcf0c2d6c4b6a100000000000000000000000000000000000000000000000000000000000000c499585aac00000000000000000000000000000000000000000000000af10f7eb24f506cfd000000000000000000000000000000000000
<= 9000
=> e004800096000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000111111111117dc0aa78b770fa6a738034120c302000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000
<= 9000
=> e00480009600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000010000
<= 9000
=> e00480006100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076c65646765723200000000000000000000000000000000000000000000000000018080
<= 26d9e62b0b6ae0c18d3d2ecdf20ce7f1c959e0f609b4e73e2d138bbdc3e1e9390012469e2124a8955b5159f670b0333b803a70dd7dc51558a8f7460b27eed77be59000`.toLowerCase();

test("paraswap", async () => {
const transport = await openTransportReplayer(
RecordStore.fromString(
`
=> e0120000670850617261737761701bd435f3c054b6e901b7b108a0ab7617c808677bcfc0afeb304402201c0cbe69aac517825b3a6eb5e7251e8fd57ff93a43bd3df52c7a841818eda81b022001a10cc326efaee2463fc96e7c29739c308fb8179bd2ac37303662bae4f7705c
<= 9000
=> e00a0000680531494e4348111111111117dc0aa78b770fa6a738034120c3020000001200000001304402204623e5f1375c54a446157ae8a739204284cf053634b7abd083dc5f5d2675c4e702206ff94b4c84ba9e93f44065c38d7c92506621fa69ba04f767aa58221de8afbf17
<= 9000
=> e004000096058000002c8000003c800000000000000000000000f903cd82043d8509c765240083042e73941bd435f3c054b6e901b7b108a0ab7617c808677b80b903a4cfc0afeb000000000000000000000000111111111117dc0aa78b770fa6a738034120c302000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000
<= 9000
=> e0048000960000000af10f7eb24f506cfd00000000000000000000000000000000000000000000000002a5b905b3c9fa4c00000000000000000000000000000000000000000000000002baaee8d905020a000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000
<= 9000
=> e004800096000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000000
<= 9000
=> e004800096000000000000000000000000000000000000000000000000000000000000000100000000000000000000000086d3579b043585a97532514016dcf0c2d6c4b6a100000000000000000000000000000000000000000000000000000000000000c499585aac00000000000000000000000000000000000000000000000af10f7eb24f506cfd000000000000000000000000000000000000
<= 9000
=> e004800096000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000111111111117dc0aa78b770fa6a738034120c302000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000
<= 9000
=> e00480009600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c400000000000000000000000000000000000000000000000000000000000000010000
<= 9000
=> e00480006100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076c65646765723200000000000000000000000000000000000000000000000000018080
<= 26d9e62b0b6ae0c18d3d2ecdf20ce7f1c959e0f609b4e73e2d138bbdc3e1e9390012469e2124a8955b5159f670b0333b803a70dd7dc51558a8f7460b27eed77be59000
`.toLowerCase()
)
RecordStore.fromString(paraswapAPDUs)
);
const eth = new Eth(transport);
const result = await eth.signTransaction(
"44'/60'/0'/0/0",
"f903cd82043d8509c765240083042e73941bd435f3c054b6e901b7b108a0ab7617c808677b80b903a4cfc0afeb000000000000000000000000111111111117dc0aa78b770fa6a738034120c302000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000000000000000000000000000af10f7eb24f506cfd00000000000000000000000000000000000000000000000002a5b905b3c9fa4c00000000000000000000000000000000000000000000000002baaee8d905020a000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000086d3579b043585a97532514016dcf0c2d6c4b6a100000000000000000000000000000000000000000000000000000000000000c499585aac00000000000000000000000000000000000000000000000af10f7eb24f506cfd000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000111111111117dc0aa78b770fa6a738034120c302000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076c65646765723200000000000000000000000000000000000000000000000000018080"
);
expect(result).toEqual({
r: "d9e62b0b6ae0c18d3d2ecdf20ce7f1c959e0f609b4e73e2d138bbdc3e1e93900",
s: "12469e2124a8955b5159f670b0333b803a70dd7dc51558a8f7460b27eed77be5",
v: "26",
});
});

test("paraswap without plugins", async () => {
const transport = await openTransportReplayer(
// skip 2 first apdu that is used for plugins
RecordStore.fromString(paraswapAPDUs.split("\n").slice(4).join("\n"))
);
const eth = new Eth(transport);
eth.setPluginsLoadConfig({
baseURL: null,
});
const result = await eth.signTransaction(
"44'/60'/0'/0/0",
"f903cd82043d8509c765240083042e73941bd435f3c054b6e901b7b108a0ab7617c808677b80b903a4cfc0afeb000000000000000000000000111111111117dc0aa78b770fa6a738034120c302000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000000000000000000000000000af10f7eb24f506cfd00000000000000000000000000000000000000000000000002a5b905b3c9fa4c00000000000000000000000000000000000000000000000002baaee8d905020a000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000086d3579b043585a97532514016dcf0c2d6c4b6a100000000000000000000000000000000000000000000000000000000000000c499585aac00000000000000000000000000000000000000000000000af10f7eb24f506cfd000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000111111111117dc0aa78b770fa6a738034120c302000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076c65646765723200000000000000000000000000000000000000000000000000018080"
);
expect(result).toEqual({
r: "d9e62b0b6ae0c18d3d2ecdf20ce7f1c959e0f609b4e73e2d138bbdc3e1e93900",
s: "12469e2124a8955b5159f670b0333b803a70dd7dc51558a8f7460b27eed77be5",
v: "26",
});
});

test("paraswap without plugins CDN but with explicit plugin", async () => {
const transport = await openTransportReplayer(
// behave like paraswap test
RecordStore.fromString(paraswapAPDUs)
);
const eth = new Eth(transport);
eth.setPluginsLoadConfig({
baseURL: null,
extraPlugins: paraswapJSON,
});
const result = await eth.signTransaction(
"44'/60'/0'/0/0",
"f903cd82043d8509c765240083042e73941bd435f3c054b6e901b7b108a0ab7617c808677b80b903a4cfc0afeb000000000000000000000000111111111117dc0aa78b770fa6a738034120c302000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee00000000000000000000000000000000000000000000000af10f7eb24f506cfd00000000000000000000000000000000000000000000000002a5b905b3c9fa4c00000000000000000000000000000000000000000000000002baaee8d905020a000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000086d3579b043585a97532514016dcf0c2d6c4b6a100000000000000000000000000000000000000000000000000000000000000c499585aac00000000000000000000000000000000000000000000000af10f7eb24f506cfd000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000002000000000000000000000000111111111117dc0aa78b770fa6a738034120c302000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c40000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076c65646765723200000000000000000000000000000000000000000000000000018080"
Expand Down
Loading