@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.
- package/README.md +15 -3
- package/lib/Eth.d.ts +12 -5
- package/lib/Eth.d.ts.map +1 -1
- package/lib/Eth.js +95 -42
- package/lib/Eth.js.map +1 -1
- package/lib/contracts.d.ts +2 -5
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js +19 -26
- package/lib/contracts.js.map +1 -1
- package/lib/loadConfig.d.ts +7 -0
- package/lib/loadConfig.d.ts.map +1 -0
- package/lib/loadConfig.js +24 -0
- package/lib/loadConfig.js.map +1 -0
- package/lib/nfts.d.ts +11 -0
- package/lib/nfts.d.ts.map +1 -0
- package/lib/nfts.js +102 -0
- package/lib/nfts.js.map +1 -0
- package/lib-es/Eth.d.ts +12 -5
- package/lib-es/Eth.d.ts.map +1 -1
- package/lib-es/Eth.js +95 -42
- package/lib-es/Eth.js.map +1 -1
- package/lib-es/contracts.d.ts +2 -5
- package/lib-es/contracts.d.ts.map +1 -1
- package/lib-es/contracts.js +19 -26
- package/lib-es/contracts.js.map +1 -1
- package/lib-es/loadConfig.d.ts +7 -0
- package/lib-es/loadConfig.d.ts.map +1 -0
- package/lib-es/loadConfig.js +20 -0
- package/lib-es/loadConfig.js.map +1 -0
- package/lib-es/nfts.d.ts +11 -0
- package/lib-es/nfts.d.ts.map +1 -0
- package/lib-es/nfts.js +94 -0
- package/lib-es/nfts.js.map +1 -0
- package/package.json +3 -3
- package/src/Eth.ts +110 -36
- package/src/contracts.ts +18 -35
- package/src/loadConfig.ts +23 -0
- package/src/nfts.ts +64 -0
- package/tests/Eth.test.ts +4 -4
package/lib-es/contracts.js
CHANGED
|
@@ -46,37 +46,30 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
48
|
import axios from "axios";
|
|
49
|
-
|
|
50
|
-
|
|
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,
|
|
57
|
-
var _a,
|
|
58
|
-
return __generator(this, function (
|
|
59
|
-
switch (
|
|
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 =
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
}
|
package/lib-es/contracts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contracts.js","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,KAAK,MAAM,OAAO,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 @@
|
|
|
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"}
|
package/lib-es/nfts.d.ts
ADDED
|
@@ -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.
|
|
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.
|
|
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": "
|
|
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 {
|
|
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
|
-
|
|
73
|
+
loadConfig: LoadConfig;
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
this.
|
|
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
|
-
|
|
82
|
+
loadConfig: LoadConfig = {}
|
|
82
83
|
) {
|
|
83
84
|
this.transport = transport;
|
|
84
|
-
this.
|
|
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
|
|
312
|
-
|
|
313
|
+
const nftInfo = await getNFTInfo(
|
|
314
|
+
address,
|
|
315
|
+
chainIdTruncated,
|
|
316
|
+
this.loadConfig
|
|
317
|
+
);
|
|
318
|
+
if (nftInfo) {
|
|
313
319
|
log(
|
|
314
320
|
"ethereum",
|
|
315
|
-
"loading
|
|
316
|
-
|
|
321
|
+
"loading nft info for " +
|
|
322
|
+
nftInfo.contractAddress +
|
|
317
323
|
" (" +
|
|
318
|
-
|
|
324
|
+
nftInfo.collectionName +
|
|
319
325
|
")"
|
|
320
326
|
);
|
|
321
|
-
await
|
|
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
|
|
349
|
+
const nftPluginPayload = await loadNftPlugin(
|
|
328
350
|
decodedTx.to,
|
|
329
351
|
selector,
|
|
330
352
|
chainIdTruncated,
|
|
331
|
-
this.
|
|
353
|
+
this.loadConfig
|
|
332
354
|
);
|
|
333
355
|
|
|
334
|
-
if (
|
|
335
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
369
|
+
if (plugin) {
|
|
370
|
+
log("ethereum", "loading plugin for " + selector);
|
|
371
|
+
await setExternalPlugin(this.transport, payload, signature);
|
|
372
|
+
}
|
|
341
373
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
21
|
+
userLoadConfig: LoadConfig
|
|
34
22
|
): Promise<ContractMethod | undefined> => {
|
|
35
|
-
const {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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.
|
|
170
|
-
|
|
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.
|
|
190
|
-
|
|
189
|
+
eth.setLoadConfig({
|
|
190
|
+
pluginBaseURL: null,
|
|
191
191
|
extraPlugins: paraswapJSON,
|
|
192
192
|
});
|
|
193
193
|
const result = await eth.signTransaction(
|