@ledgerhq/hw-app-eth 6.13.0 → 6.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,37 +46,30 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
46
46
  }
47
47
  };
48
48
  import axios from "axios";
49
- var defaultPluginsLoadConfig = {
50
- baseURL: "https://cdn.live.ledger.com",
51
- extraPlugins: null
52
- };
49
+ import { getLoadConfig } from "./loadConfig";
50
+ import { log } from "@ledgerhq/logs";
53
51
  /**
54
52
  * Retrieve the metadatas a given contract address and a method selector
55
53
  */
56
- export var loadInfosForContractMethod = function (contractAddress, selector, chainId, userPluginsLoadConfig) { return __awaiter(void 0, void 0, void 0, function () {
57
- var _a, baseURL, extraPlugins, data, _b, lcSelector, lcContractAddress, contractSelectors;
58
- return __generator(this, function (_c) {
59
- switch (_c.label) {
54
+ export var loadInfosForContractMethod = function (contractAddress, selector, chainId, userLoadConfig) { return __awaiter(void 0, void 0, void 0, function () {
55
+ var _a, pluginBaseURL, extraPlugins, data, url_1, lcSelector, lcContractAddress, contractSelectors;
56
+ return __generator(this, function (_b) {
57
+ switch (_b.label) {
60
58
  case 0:
61
- _a = __assign(__assign({}, defaultPluginsLoadConfig), userPluginsLoadConfig), baseURL = _a.baseURL, extraPlugins = _a.extraPlugins;
62
- if (!!baseURL) return [3 /*break*/, 1];
63
- _b = {};
64
- return [3 /*break*/, 3];
65
- case 1: return [4 /*yield*/, axios
66
- .get(baseURL + "/plugins/ethereum.json")
67
- .then(function (r) { return r.data; })["catch"](function (e) {
68
- if (e.response &&
69
- 400 <= e.response.status &&
70
- e.response.status < 500) {
71
- return null; // not found cases can be ignored to allow future changes in endpoint without failing a signature to be done.
72
- }
73
- throw e;
74
- })];
59
+ _a = getLoadConfig(userLoadConfig), pluginBaseURL = _a.pluginBaseURL, extraPlugins = _a.extraPlugins;
60
+ data = {};
61
+ if (!pluginBaseURL) return [3 /*break*/, 2];
62
+ url_1 = pluginBaseURL + "/plugins/ethereum.json";
63
+ return [4 /*yield*/, axios
64
+ .get(pluginBaseURL + "/plugins/ethereum.json")
65
+ .then(function (r) { return r.data; })["catch"](function (e) {
66
+ log("error", "could not fetch from " + url_1 + ": " + String(e));
67
+ return null;
68
+ })];
69
+ case 1:
70
+ data = _b.sent();
71
+ _b.label = 2;
75
72
  case 2:
76
- _b = _c.sent();
77
- _c.label = 3;
78
- case 3:
79
- data = _b;
80
73
  if (extraPlugins) {
81
74
  data = __assign(__assign({}, data), extraPlugins);
82
75
  }
@@ -1 +1 @@
1
- {"version":3,"file":"contracts.js","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAoB1B,IAAM,wBAAwB,GAAG;IAC/B,OAAO,EAAE,6BAA6B;IACtC,YAAY,EAAE,IAAI;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,IAAM,0BAA0B,GAAG,UACxC,eAAuB,EACvB,QAAgB,EAChB,OAAe,EACf,qBAAwC;;;;;gBAElC,2BACD,wBAAwB,GACxB,qBAAqB,CACzB,EAHO,OAAO,aAAA,EAAE,YAAY,kBAAA,CAG3B;qBACS,CAAC,OAAO,EAAR,wBAAQ;gBACf,KAAA,EAAE,CAAA;;oBACF,qBAAM,KAAK;qBACR,GAAG,CAAI,OAAO,2BAAwB,CAAC;qBACvC,IAAI,CAAC,UAAC,CAAC,IAAK,OAAA,CAAC,CAAC,IAAW,EAAb,CAAa,CAAC,CAC1B,OAAK,CAAA,CAAC,UAAC,CAAC;oBACP,IACE,CAAC,CAAC,QAAQ;wBACV,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM;wBACxB,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,GAAG,EACvB;wBACA,OAAO,IAAI,CAAC,CAAC,6GAA6G;qBAC3H;oBACD,MAAM,CAAC,CAAC;gBACV,CAAC,CAAC,EAAA;;gBAZJ,KAAA,SAYI,CAAA;;;gBAdJ,IAAI,KAcA;gBAER,IAAI,YAAY,EAAE;oBAChB,IAAI,yBAAQ,IAAI,GAAK,YAAY,CAAE,CAAC;iBACrC;gBAED,IAAI,CAAC,IAAI;oBAAE,sBAAO;gBAEZ,UAAU,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;gBACpC,iBAAiB,GAAG,eAAe,CAAC,WAAW,EAAE,CAAC;gBAExD,IAAI,iBAAiB,IAAI,IAAI,EAAE;oBACvB,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC;oBAElD,IAAI,UAAU,IAAI,iBAAiB,EAAE;wBACnC,sBAAO;gCACL,OAAO,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,iBAAiB,CAAC;gCACzD,SAAS,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC;gCACrD,MAAM,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC;gCAC/C,eAAe,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,iBAAiB,CAAC;gCACjE,GAAG,EAAE,iBAAiB,CAAC,KAAK,CAAC;6BAC9B,EAAC;qBACH;iBACF;;;;KACF,CAAC"}
1
+ {"version":3,"file":"contracts.js","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAUrC;;GAEG;AACH,MAAM,CAAC,IAAM,0BAA0B,GAAG,UACxC,eAAuB,EACvB,QAAgB,EAChB,OAAe,EACf,cAA0B;;;;;gBAEpB,KAAkC,aAAa,CAAC,cAAc,CAAC,EAA7D,aAAa,mBAAA,EAAE,YAAY,kBAAA,CAAmC;gBAElE,IAAI,GAAG,EAAE,CAAC;qBAEV,aAAa,EAAb,wBAAa;gBACT,QAAS,aAAa,2BAAwB,CAAC;gBAC9C,qBAAM,KAAK;yBACf,GAAG,CAAI,aAAa,2BAAwB,CAAC;yBAC7C,IAAI,CAAC,UAAC,CAAC,IAAK,OAAA,CAAC,CAAC,IAAW,EAAb,CAAa,CAAC,CAC1B,OAAK,CAAA,CAAC,UAAC,CAAC;wBACP,GAAG,CAAC,OAAO,EAAE,uBAAuB,GAAG,KAAG,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;wBAC/D,OAAO,IAAI,CAAC;oBACd,CAAC,CAAC,EAAA;;gBANJ,IAAI,GAAG,SAMH,CAAC;;;gBAGP,IAAI,YAAY,EAAE;oBAChB,IAAI,yBAAQ,IAAI,GAAK,YAAY,CAAE,CAAC;iBACrC;gBAED,IAAI,CAAC,IAAI;oBAAE,sBAAO;gBAEZ,UAAU,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;gBACpC,iBAAiB,GAAG,eAAe,CAAC,WAAW,EAAE,CAAC;gBAExD,IAAI,iBAAiB,IAAI,IAAI,EAAE;oBACvB,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC;oBAElD,IAAI,UAAU,IAAI,iBAAiB,EAAE;wBACnC,sBAAO;gCACL,OAAO,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,iBAAiB,CAAC;gCACzD,SAAS,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC;gCACrD,MAAM,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC;gCAC/C,eAAe,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC,iBAAiB,CAAC;gCACjE,GAAG,EAAE,iBAAiB,CAAC,KAAK,CAAC;6BAC9B,EAAC;qBACH;iBACF;;;;KACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare type LoadConfig = {
2
+ nftExplorerBaseURL?: string | null;
3
+ pluginBaseURL?: string | null;
4
+ extraPlugins?: any | null;
5
+ };
6
+ export declare function getLoadConfig(userLoadConfig?: LoadConfig): LoadConfig;
7
+ //# sourceMappingURL=loadConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadConfig.d.ts","sourceRoot":"","sources":["../src/loadConfig.ts"],"names":[],"mappings":"AAAA,oBAAY,UAAU,GAAG;IACvB,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAInC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAG9B,YAAY,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;CAC3B,CAAC;AAQF,wBAAgB,aAAa,CAAC,cAAc,CAAC,EAAE,UAAU,GAAG,UAAU,CAKrE"}
@@ -0,0 +1,20 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ var defaultLoadConfig = {
13
+ nftExplorerBaseURL: null,
14
+ pluginBaseURL: "https://cdn.live.ledger.com",
15
+ extraPlugins: null
16
+ };
17
+ export function getLoadConfig(userLoadConfig) {
18
+ return __assign(__assign({}, defaultLoadConfig), userLoadConfig);
19
+ }
20
+ //# sourceMappingURL=loadConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadConfig.js","sourceRoot":"","sources":["../src/loadConfig.ts"],"names":[],"mappings":";;;;;;;;;;;AAWA,IAAM,iBAAiB,GAAG;IACxB,kBAAkB,EAAE,IAAI;IACxB,aAAa,EAAE,6BAA6B;IAC5C,YAAY,EAAE,IAAI;CACnB,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,cAA2B;IACvD,6BACK,iBAAiB,GACjB,cAAc,EACjB;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ /// <reference types="node" />
2
+ import type { LoadConfig } from "./loadConfig";
3
+ declare type NftInfo = {
4
+ contractAddress: string;
5
+ collectionName: string;
6
+ data: Buffer;
7
+ };
8
+ export declare const getNFTInfo: (contractAddress: string, chainId: number, userLoadConfig: LoadConfig) => Promise<NftInfo | undefined>;
9
+ export declare const loadNftPlugin: (contractAddress: string, selector: string, chainId: number, userLoadConfig: LoadConfig) => Promise<string | undefined>;
10
+ export {};
11
+ //# sourceMappingURL=nfts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nfts.d.ts","sourceRoot":"","sources":["../src/nfts.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C,aAAK,OAAO,GAAG;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAMF,eAAO,MAAM,UAAU,oBACJ,MAAM,WACd,MAAM,kBACC,UAAU,KACzB,QAAQ,OAAO,GAAG,SAAS,CAqB7B,CAAC;AAEF,eAAO,MAAM,aAAa,oBACP,MAAM,YACb,MAAM,WACP,MAAM,kBACC,UAAU,KACzB,QAAQ,MAAM,GAAG,SAAS,CAgB5B,CAAC"}
package/lib-es/nfts.js ADDED
@@ -0,0 +1,94 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ var __generator = (this && this.__generator) || function (thisArg, body) {
11
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
12
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
13
+ function verb(n) { return function (v) { return step([n, v]); }; }
14
+ function step(op) {
15
+ if (f) throw new TypeError("Generator is already executing.");
16
+ while (_) try {
17
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
18
+ if (y = 0, t) op = [op[0] & 2, t.value];
19
+ switch (op[0]) {
20
+ case 0: case 1: t = op; break;
21
+ case 4: _.label++; return { value: op[1], done: false };
22
+ case 5: _.label++; y = op[1]; op = [0]; continue;
23
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
24
+ default:
25
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
26
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
27
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
28
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
29
+ if (t[2]) _.ops.pop();
30
+ _.trys.pop(); continue;
31
+ }
32
+ op = body.call(thisArg, _);
33
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
34
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
35
+ }
36
+ };
37
+ import axios from "axios";
38
+ import { getLoadConfig } from "./loadConfig";
39
+ import { log } from "@ledgerhq/logs";
40
+ export var getNFTInfo = function (contractAddress, chainId, userLoadConfig) { return __awaiter(void 0, void 0, void 0, function () {
41
+ var nftExplorerBaseURL, url, response, payload, collectionNameLength, collectionName;
42
+ return __generator(this, function (_a) {
43
+ switch (_a.label) {
44
+ case 0:
45
+ nftExplorerBaseURL = getLoadConfig(userLoadConfig).nftExplorerBaseURL;
46
+ if (!nftExplorerBaseURL)
47
+ return [2 /*return*/];
48
+ url = nftExplorerBaseURL + "/" + chainId + "/contracts/" + contractAddress;
49
+ return [4 /*yield*/, axios
50
+ .get(url)
51
+ .then(function (r) { return r.data; })["catch"](function (e) {
52
+ log("error", "could not fetch from " + url + ": " + String(e));
53
+ return null;
54
+ })];
55
+ case 1:
56
+ response = _a.sent();
57
+ if (!response)
58
+ return [2 /*return*/];
59
+ payload = response["payload"];
60
+ collectionNameLength = Number(payload.slice(2, 3));
61
+ collectionName = payload.slice(3, 3 + collectionNameLength).toString();
62
+ return [2 /*return*/, {
63
+ contractAddress: contractAddress,
64
+ collectionName: collectionName,
65
+ data: Buffer.from(payload, "hex")
66
+ }];
67
+ }
68
+ });
69
+ }); };
70
+ export var loadNftPlugin = function (contractAddress, selector, chainId, userLoadConfig) { return __awaiter(void 0, void 0, void 0, function () {
71
+ var nftExplorerBaseURL, url, response, payload;
72
+ return __generator(this, function (_a) {
73
+ switch (_a.label) {
74
+ case 0:
75
+ nftExplorerBaseURL = getLoadConfig(userLoadConfig).nftExplorerBaseURL;
76
+ if (!nftExplorerBaseURL)
77
+ return [2 /*return*/];
78
+ url = nftExplorerBaseURL + "/" + chainId + "/contracts/" + contractAddress + "/plugin/selector?selector=" + selector;
79
+ return [4 /*yield*/, axios
80
+ .get(url)
81
+ .then(function (r) { return r.data; })["catch"](function (e) {
82
+ log("error", "could not fetch from " + url + ": " + String(e));
83
+ return null;
84
+ })];
85
+ case 1:
86
+ response = _a.sent();
87
+ if (!response)
88
+ return [2 /*return*/];
89
+ payload = response["payload"];
90
+ return [2 /*return*/, payload];
91
+ }
92
+ });
93
+ }); };
94
+ //# sourceMappingURL=nfts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nfts.js","sourceRoot":"","sources":["../src/nfts.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAYrC,MAAM,CAAC,IAAM,UAAU,GAAG,UACxB,eAAuB,EACvB,OAAe,EACf,cAA0B;;;;;gBAElB,kBAAkB,GAAK,aAAa,CAAC,cAAc,CAAC,mBAAlC,CAAmC;gBAC7D,IAAI,CAAC,kBAAkB;oBAAE,sBAAO;gBAC1B,GAAG,GAAM,kBAAkB,SAAI,OAAO,mBAAc,eAAiB,CAAC;gBAC3D,qBAAM,KAAK;yBACzB,GAAG,CAAkB,GAAG,CAAC;yBACzB,IAAI,CAAC,UAAC,CAAC,IAAK,OAAA,CAAC,CAAC,IAAI,EAAN,CAAM,CAAC,CACnB,OAAK,CAAA,CAAC,UAAC,CAAC;wBACP,GAAG,CAAC,OAAO,EAAE,uBAAuB,GAAG,GAAG,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;wBAC/D,OAAO,IAAI,CAAC;oBACd,CAAC,CAAC,EAAA;;gBANE,QAAQ,GAAG,SAMb;gBACJ,IAAI,CAAC,QAAQ;oBAAE,sBAAO;gBAEhB,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;gBAC9B,oBAAoB,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBACnD,cAAc,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,oBAAoB,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC7E,sBAAO;wBACL,eAAe,EAAE,eAAe;wBAChC,cAAc,EAAE,cAAc;wBAC9B,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC;qBAClC,EAAC;;;KACH,CAAC;AAEF,MAAM,CAAC,IAAM,aAAa,GAAG,UAC3B,eAAuB,EACvB,QAAgB,EAChB,OAAe,EACf,cAA0B;;;;;gBAElB,kBAAkB,GAAK,aAAa,CAAC,cAAc,CAAC,mBAAlC,CAAmC;gBAC7D,IAAI,CAAC,kBAAkB;oBAAE,sBAAO;gBAC1B,GAAG,GAAM,kBAAkB,SAAI,OAAO,mBAAc,eAAe,kCAA6B,QAAU,CAAC;gBAEhG,qBAAM,KAAK;yBACzB,GAAG,CAAkB,GAAG,CAAC;yBACzB,IAAI,CAAC,UAAC,CAAC,IAAK,OAAA,CAAC,CAAC,IAAI,EAAN,CAAM,CAAC,CACnB,OAAK,CAAA,CAAC,UAAC,CAAC;wBACP,GAAG,CAAC,OAAO,EAAE,uBAAuB,GAAG,GAAG,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;wBAC/D,OAAO,IAAI,CAAC;oBACd,CAAC,CAAC,EAAA;;gBANE,QAAQ,GAAG,SAMb;gBACJ,IAAI,CAAC,QAAQ;oBAAE,sBAAO;gBAEhB,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;gBACpC,sBAAO,OAAO,EAAC;;;KAChB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ledgerhq/hw-app-eth",
3
- "version": "6.13.0",
3
+ "version": "6.14.0",
4
4
  "description": "Ledger Hardware Wallet Ethereum Application API",
5
5
  "keywords": [
6
6
  "Ledger",
@@ -27,7 +27,7 @@
27
27
  "types": "lib/Eth.d.ts",
28
28
  "license": "Apache-2.0",
29
29
  "dependencies": {
30
- "@ledgerhq/cryptoassets": "^6.13.0",
30
+ "@ledgerhq/cryptoassets": "^6.14.0",
31
31
  "@ledgerhq/errors": "^6.10.0",
32
32
  "@ledgerhq/hw-transport": "^6.11.2",
33
33
  "@ledgerhq/logs": "^6.10.0",
@@ -41,5 +41,5 @@
41
41
  "watch": "bash ../../script/watch.sh",
42
42
  "doc": "bash ../../script/doc.sh"
43
43
  },
44
- "gitHead": "c090d9eaf93cebaa909fc97ac8a18351d8750965"
44
+ "gitHead": "5cbdfefa5b9961fde122e4005f21b10af6c20019"
45
45
  }
package/src/Eth.ts CHANGED
@@ -23,7 +23,8 @@ import { BigNumber } from "bignumber.js";
23
23
  import { ethers } from "ethers";
24
24
  import { byContractAddressAndChainId } from "./erc20";
25
25
  import { loadInfosForContractMethod } from "./contracts";
26
- import type { PluginsLoadConfig } from "./contracts";
26
+ import type { LoadConfig } from "./loadConfig";
27
+ import { getNFTInfo, loadNftPlugin } from "./nfts";
27
28
 
28
29
  export type StarkQuantizationType =
29
30
  | "eth"
@@ -69,19 +70,19 @@ const remapTransactionRelatedErrors = (e) => {
69
70
 
70
71
  export default class Eth {
71
72
  transport: Transport;
72
- pluginsLoadConfig: PluginsLoadConfig;
73
+ loadConfig: LoadConfig;
73
74
 
74
- setPluginsLoadConfig(pluginsLoadConfig: PluginsLoadConfig): void {
75
- this.pluginsLoadConfig = pluginsLoadConfig;
75
+ setLoadConfig(loadConfig: LoadConfig): void {
76
+ this.loadConfig = loadConfig;
76
77
  }
77
78
 
78
79
  constructor(
79
80
  transport: Transport,
80
81
  scrambleKey = "w0w",
81
- pluginsLoadConfig: PluginsLoadConfig = {}
82
+ loadConfig: LoadConfig = {}
82
83
  ) {
83
84
  this.transport = transport;
84
- this.pluginsLoadConfig = pluginsLoadConfig;
85
+ this.loadConfig = loadConfig;
85
86
  transport.decorateAppAPIMethods(
86
87
  this,
87
88
  [
@@ -102,6 +103,7 @@ export default class Eth {
102
103
  "eth2GetPublicKey",
103
104
  "eth2SetWithdrawalIndex",
104
105
  "setExternalPlugin",
106
+ "setPlugin",
105
107
  ],
106
108
  scrambleKey
107
109
  );
@@ -308,55 +310,85 @@ export default class Eth {
308
310
  }
309
311
 
310
312
  const provideForContract = async (address) => {
311
- const erc20Info = byContractAddressAndChainId(address, chainIdTruncated);
312
- if (erc20Info) {
313
+ const nftInfo = await getNFTInfo(
314
+ address,
315
+ chainIdTruncated,
316
+ this.loadConfig
317
+ );
318
+ if (nftInfo) {
313
319
  log(
314
320
  "ethereum",
315
- "loading erc20token info for " +
316
- erc20Info.contractAddress +
321
+ "loading nft info for " +
322
+ nftInfo.contractAddress +
317
323
  " (" +
318
- erc20Info.ticker +
324
+ nftInfo.collectionName +
319
325
  ")"
320
326
  );
321
- await provideERC20TokenInformation(this.transport, erc20Info.data);
327
+ await provideNFTInformation(this.transport, nftInfo.data);
328
+ } else {
329
+ const erc20Info = byContractAddressAndChainId(
330
+ address,
331
+ chainIdTruncated
332
+ );
333
+ if (erc20Info) {
334
+ log(
335
+ "ethereum",
336
+ "loading erc20token info for " +
337
+ erc20Info.contractAddress +
338
+ " (" +
339
+ erc20Info.ticker +
340
+ ")"
341
+ );
342
+ await provideERC20TokenInformation(this.transport, erc20Info.data);
343
+ }
322
344
  }
323
345
  };
324
346
 
325
347
  if (decodedTx.data.length >= 10) {
326
348
  const selector = decodedTx.data.substring(0, 10);
327
- const infos = await loadInfosForContractMethod(
349
+ const nftPluginPayload = await loadNftPlugin(
328
350
  decodedTx.to,
329
351
  selector,
330
352
  chainIdTruncated,
331
- this.pluginsLoadConfig
353
+ this.loadConfig
332
354
  );
333
355
 
334
- if (infos) {
335
- const { plugin, payload, signature, erc20OfInterest, abi } = infos;
356
+ if (nftPluginPayload) {
357
+ setPlugin(this.transport, nftPluginPayload);
358
+ } else {
359
+ const infos = await loadInfosForContractMethod(
360
+ decodedTx.to,
361
+ selector,
362
+ chainIdTruncated,
363
+ this.loadConfig
364
+ );
365
+
366
+ if (infos) {
367
+ const { plugin, payload, signature, erc20OfInterest, abi } = infos;
336
368
 
337
- if (plugin) {
338
- log("ethereum", "loading plugin for " + selector);
339
- await setExternalPlugin(this.transport, payload, signature);
340
- }
369
+ if (plugin) {
370
+ log("ethereum", "loading plugin for " + selector);
371
+ await setExternalPlugin(this.transport, payload, signature);
372
+ }
341
373
 
342
- if (erc20OfInterest && erc20OfInterest.length && abi) {
343
- const contract = new ethers.utils.Interface(abi);
344
- const args = contract.parseTransaction(decodedTx).args;
345
-
346
- for (path of erc20OfInterest) {
347
- const address = path.split(".").reduce((value, seg) => {
348
- if (seg === "-1" && Array.isArray(value)) {
349
- return value[value.length - 1];
350
- }
351
- return value[seg];
352
- }, args);
353
- await provideForContract(address);
374
+ if (erc20OfInterest && erc20OfInterest.length && abi) {
375
+ const contract = new ethers.utils.Interface(abi);
376
+ const args = contract.parseTransaction(decodedTx).args;
377
+
378
+ for (path of erc20OfInterest) {
379
+ const address = path.split(".").reduce((value, seg) => {
380
+ if (seg === "-1" && Array.isArray(value)) {
381
+ return value[value.length - 1];
382
+ }
383
+ return value[seg];
384
+ }, args);
385
+ await provideForContract(address);
386
+ }
354
387
  }
388
+ } else {
389
+ log("ethereum", "no infos for selector " + selector);
355
390
  }
356
- } else {
357
- log("ethereum", "no infos for selector " + selector);
358
391
  }
359
-
360
392
  await provideForContract(decodedTx.to);
361
393
  }
362
394
 
@@ -1225,7 +1257,7 @@ export default class Eth {
1225
1257
  }
1226
1258
 
1227
1259
  /**
1228
- * Set the name of the plugin that should be used to parse the next transaction
1260
+ * Set the name of the external plugin that should be used to parse the next transaction
1229
1261
  *
1230
1262
  * @param pluginName string containing the name of the plugin, must have length between 1 and 30 bytes
1231
1263
  * @return True if the method was executed successfully
@@ -1237,6 +1269,16 @@ export default class Eth {
1237
1269
  ): Promise<boolean> {
1238
1270
  return setExternalPlugin(this.transport, pluginName, selector);
1239
1271
  }
1272
+
1273
+ /**
1274
+ * Set the plugin (internal or external) that should be used to parse the next transaction
1275
+ *
1276
+ * @param data string containing the payload and signature that will be parsed and verified by the device.
1277
+ * @return True if the method was executed successfully
1278
+ */
1279
+ setPlugin(data: string): Promise<boolean> {
1280
+ return setPlugin(this.transport, data);
1281
+ }
1240
1282
  }
1241
1283
 
1242
1284
  // internal helpers
@@ -1258,6 +1300,18 @@ function provideERC20TokenInformation(
1258
1300
  );
1259
1301
  }
1260
1302
 
1303
+ function provideNFTInformation(
1304
+ transport: Transport,
1305
+ data: Buffer
1306
+ ): Promise<boolean> {
1307
+ return transport.send(0xe0, 0x14, 0x00, 0x00, data).then(
1308
+ () => true,
1309
+ (e) => {
1310
+ throw e;
1311
+ }
1312
+ );
1313
+ }
1314
+
1261
1315
  function setExternalPlugin(
1262
1316
  transport: Transport,
1263
1317
  payload: string,
@@ -1283,3 +1337,23 @@ function setExternalPlugin(
1283
1337
  }
1284
1338
  );
1285
1339
  }
1340
+
1341
+ function setPlugin(transport: Transport, data: string): Promise<boolean> {
1342
+ const buffer = Buffer.from(data, "hex");
1343
+ return transport.send(0xe0, 0x16, 0x00, 0x00, buffer).then(
1344
+ () => true,
1345
+ (e) => {
1346
+ if (e && e.statusCode === 0x6a80) {
1347
+ // this case happen when the plugin name is too short or too long
1348
+ return false;
1349
+ } else if (e && e.statusCode === 0x6984) {
1350
+ // this case happen when the plugin requested is not installed on the device
1351
+ return false;
1352
+ } else if (e && e.statusCode === 0x6d00) {
1353
+ // this case happen for older version of ETH app
1354
+ return false;
1355
+ }
1356
+ throw e;
1357
+ }
1358
+ );
1359
+ }
package/src/contracts.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import axios from "axios";
2
+ import { getLoadConfig } from "./loadConfig";
3
+ import type { LoadConfig } from "./loadConfig";
4
+ import { log } from "@ledgerhq/logs";
2
5
 
3
6
  type ContractMethod = {
4
7
  payload: string;
@@ -8,21 +11,6 @@ type ContractMethod = {
8
11
  abi: any;
9
12
  };
10
13
 
11
- // example of payload https://cdn.live.ledger.com/plugins/ethereum/1.json
12
- export type PluginsLoadConfig = {
13
- // fetch against an api (base url is an api that hosts /plugins/ethereum/${chainId}.json )
14
- // set to null will disable it
15
- baseURL?: string | null;
16
- // provide manually some extra plugins to add for the resolution (e.g. for dev purpose)
17
- // object will be merged with the returned value of the Ledger cdn payload
18
- extraPlugins?: any | null;
19
- };
20
-
21
- const defaultPluginsLoadConfig = {
22
- baseURL: "https://cdn.live.ledger.com",
23
- extraPlugins: null,
24
- };
25
-
26
14
  /**
27
15
  * Retrieve the metadatas a given contract address and a method selector
28
16
  */
@@ -30,27 +18,22 @@ export const loadInfosForContractMethod = async (
30
18
  contractAddress: string,
31
19
  selector: string,
32
20
  chainId: number,
33
- userPluginsLoadConfig: PluginsLoadConfig
21
+ userLoadConfig: LoadConfig
34
22
  ): Promise<ContractMethod | undefined> => {
35
- const { baseURL, extraPlugins } = {
36
- ...defaultPluginsLoadConfig,
37
- ...userPluginsLoadConfig,
38
- };
39
- let data = !baseURL
40
- ? {}
41
- : await axios
42
- .get(`${baseURL}/plugins/ethereum.json`)
43
- .then((r) => r.data as any)
44
- .catch((e) => {
45
- if (
46
- e.response &&
47
- 400 <= e.response.status &&
48
- e.response.status < 500
49
- ) {
50
- return null; // not found cases can be ignored to allow future changes in endpoint without failing a signature to be done.
51
- }
52
- throw e;
53
- });
23
+ const { pluginBaseURL, extraPlugins } = getLoadConfig(userLoadConfig);
24
+
25
+ let data = {};
26
+
27
+ if (pluginBaseURL) {
28
+ const url = `${pluginBaseURL}/plugins/ethereum.json`;
29
+ data = await axios
30
+ .get(`${pluginBaseURL}/plugins/ethereum.json`)
31
+ .then((r) => r.data as any)
32
+ .catch((e) => {
33
+ log("error", "could not fetch from " + url + ": " + String(e));
34
+ return null;
35
+ });
36
+ }
54
37
 
55
38
  if (extraPlugins) {
56
39
  data = { ...data, ...extraPlugins };
@@ -0,0 +1,23 @@
1
+ export type LoadConfig = {
2
+ nftExplorerBaseURL?: string | null;
3
+ // example of payload https://cdn.live.ledger.com/plugins/ethereum/1.json
4
+ // fetch against an api (base url is an api that hosts /plugins/ethereum/${chainId}.json )
5
+ // set to null will disable it
6
+ pluginBaseURL?: string | null;
7
+ // provide manually some extra plugins to add for the resolution (e.g. for dev purpose)
8
+ // object will be merged with the returned value of the Ledger cdn payload
9
+ extraPlugins?: any | null;
10
+ };
11
+
12
+ const defaultLoadConfig = {
13
+ nftExplorerBaseURL: null, // set a value when an official production endpoint is released
14
+ pluginBaseURL: "https://cdn.live.ledger.com",
15
+ extraPlugins: null,
16
+ };
17
+
18
+ export function getLoadConfig(userLoadConfig?: LoadConfig): LoadConfig {
19
+ return {
20
+ ...defaultLoadConfig,
21
+ ...userLoadConfig,
22
+ };
23
+ }
package/src/nfts.ts ADDED
@@ -0,0 +1,64 @@
1
+ import axios from "axios";
2
+ import { getLoadConfig } from "./loadConfig";
3
+ import type { LoadConfig } from "./loadConfig";
4
+ import { log } from "@ledgerhq/logs";
5
+
6
+ type NftInfo = {
7
+ contractAddress: string;
8
+ collectionName: string;
9
+ data: Buffer;
10
+ };
11
+
12
+ type BackendResponse = {
13
+ payload: string;
14
+ };
15
+
16
+ export const getNFTInfo = async (
17
+ contractAddress: string,
18
+ chainId: number,
19
+ userLoadConfig: LoadConfig
20
+ ): Promise<NftInfo | undefined> => {
21
+ const { nftExplorerBaseURL } = getLoadConfig(userLoadConfig);
22
+ if (!nftExplorerBaseURL) return;
23
+ const url = `${nftExplorerBaseURL}/${chainId}/contracts/${contractAddress}`;
24
+ const response = await axios
25
+ .get<BackendResponse>(url)
26
+ .then((r) => r.data)
27
+ .catch((e) => {
28
+ log("error", "could not fetch from " + url + ": " + String(e));
29
+ return null;
30
+ });
31
+ if (!response) return;
32
+
33
+ const payload = response["payload"];
34
+ const collectionNameLength = Number(payload.slice(2, 3));
35
+ const collectionName = payload.slice(3, 3 + collectionNameLength).toString();
36
+ return {
37
+ contractAddress: contractAddress,
38
+ collectionName: collectionName,
39
+ data: Buffer.from(payload, "hex"),
40
+ };
41
+ };
42
+
43
+ export const loadNftPlugin = async (
44
+ contractAddress: string,
45
+ selector: string,
46
+ chainId: number,
47
+ userLoadConfig: LoadConfig
48
+ ): Promise<string | undefined> => {
49
+ const { nftExplorerBaseURL } = getLoadConfig(userLoadConfig);
50
+ if (!nftExplorerBaseURL) return;
51
+ const url = `${nftExplorerBaseURL}/${chainId}/contracts/${contractAddress}/plugin/selector?selector=${selector}`;
52
+
53
+ const response = await axios
54
+ .get<BackendResponse>(url)
55
+ .then((r) => r.data)
56
+ .catch((e) => {
57
+ log("error", "could not fetch from " + url + ": " + String(e));
58
+ return null;
59
+ });
60
+ if (!response) return;
61
+
62
+ const payload = response["payload"];
63
+ return payload;
64
+ };
package/tests/Eth.test.ts CHANGED
@@ -166,8 +166,8 @@ test("paraswap without plugins", async () => {
166
166
  RecordStore.fromString(paraswapAPDUs.split("\n").slice(4).join("\n"))
167
167
  );
168
168
  const eth = new Eth(transport);
169
- eth.setPluginsLoadConfig({
170
- baseURL: null,
169
+ eth.setLoadConfig({
170
+ pluginBaseURL: null,
171
171
  });
172
172
  const result = await eth.signTransaction(
173
173
  "44'/60'/0'/0/0",
@@ -186,8 +186,8 @@ test("paraswap without plugins CDN but with explicit plugin", async () => {
186
186
  RecordStore.fromString(paraswapAPDUs)
187
187
  );
188
188
  const eth = new Eth(transport);
189
- eth.setPluginsLoadConfig({
190
- baseURL: null,
189
+ eth.setLoadConfig({
190
+ pluginBaseURL: null,
191
191
  extraPlugins: paraswapJSON,
192
192
  });
193
193
  const result = await eth.signTransaction(