index.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. "use strict";
  2. import { Logger } from "@ethersproject/logger";
  3. import { version } from "./_version";
  4. const logger = new Logger(version);
  5. ///////////////////////////////
  6. function isHexable(value) {
  7. return !!(value.toHexString);
  8. }
  9. function addSlice(array) {
  10. if (array.slice) {
  11. return array;
  12. }
  13. array.slice = function () {
  14. const args = Array.prototype.slice.call(arguments);
  15. return addSlice(new Uint8Array(Array.prototype.slice.apply(array, args)));
  16. };
  17. return array;
  18. }
  19. export function isBytesLike(value) {
  20. return ((isHexString(value) && !(value.length % 2)) || isBytes(value));
  21. }
  22. function isInteger(value) {
  23. return (typeof (value) === "number" && value == value && (value % 1) === 0);
  24. }
  25. export function isBytes(value) {
  26. if (value == null) {
  27. return false;
  28. }
  29. if (value.constructor === Uint8Array) {
  30. return true;
  31. }
  32. if (typeof (value) === "string") {
  33. return false;
  34. }
  35. if (!isInteger(value.length) || value.length < 0) {
  36. return false;
  37. }
  38. for (let i = 0; i < value.length; i++) {
  39. const v = value[i];
  40. if (!isInteger(v) || v < 0 || v >= 256) {
  41. return false;
  42. }
  43. }
  44. return true;
  45. }
  46. export function arrayify(value, options) {
  47. if (!options) {
  48. options = {};
  49. }
  50. if (typeof (value) === "number") {
  51. logger.checkSafeUint53(value, "invalid arrayify value");
  52. const result = [];
  53. while (value) {
  54. result.unshift(value & 0xff);
  55. value = parseInt(String(value / 256));
  56. }
  57. if (result.length === 0) {
  58. result.push(0);
  59. }
  60. return addSlice(new Uint8Array(result));
  61. }
  62. if (options.allowMissingPrefix && typeof (value) === "string" && value.substring(0, 2) !== "0x") {
  63. value = "0x" + value;
  64. }
  65. if (isHexable(value)) {
  66. value = value.toHexString();
  67. }
  68. if (isHexString(value)) {
  69. let hex = value.substring(2);
  70. if (hex.length % 2) {
  71. if (options.hexPad === "left") {
  72. hex = "0x0" + hex.substring(2);
  73. }
  74. else if (options.hexPad === "right") {
  75. hex += "0";
  76. }
  77. else {
  78. logger.throwArgumentError("hex data is odd-length", "value", value);
  79. }
  80. }
  81. const result = [];
  82. for (let i = 0; i < hex.length; i += 2) {
  83. result.push(parseInt(hex.substring(i, i + 2), 16));
  84. }
  85. return addSlice(new Uint8Array(result));
  86. }
  87. if (isBytes(value)) {
  88. return addSlice(new Uint8Array(value));
  89. }
  90. return logger.throwArgumentError("invalid arrayify value", "value", value);
  91. }
  92. export function concat(items) {
  93. const objects = items.map(item => arrayify(item));
  94. const length = objects.reduce((accum, item) => (accum + item.length), 0);
  95. const result = new Uint8Array(length);
  96. objects.reduce((offset, object) => {
  97. result.set(object, offset);
  98. return offset + object.length;
  99. }, 0);
  100. return addSlice(result);
  101. }
  102. export function stripZeros(value) {
  103. let result = arrayify(value);
  104. if (result.length === 0) {
  105. return result;
  106. }
  107. // Find the first non-zero entry
  108. let start = 0;
  109. while (start < result.length && result[start] === 0) {
  110. start++;
  111. }
  112. // If we started with zeros, strip them
  113. if (start) {
  114. result = result.slice(start);
  115. }
  116. return result;
  117. }
  118. export function zeroPad(value, length) {
  119. value = arrayify(value);
  120. if (value.length > length) {
  121. logger.throwArgumentError("value out of range", "value", arguments[0]);
  122. }
  123. const result = new Uint8Array(length);
  124. result.set(value, length - value.length);
  125. return addSlice(result);
  126. }
  127. export function isHexString(value, length) {
  128. if (typeof (value) !== "string" || !value.match(/^0x[0-9A-Fa-f]*$/)) {
  129. return false;
  130. }
  131. if (length && value.length !== 2 + 2 * length) {
  132. return false;
  133. }
  134. return true;
  135. }
  136. const HexCharacters = "0123456789abcdef";
  137. export function hexlify(value, options) {
  138. if (!options) {
  139. options = {};
  140. }
  141. if (typeof (value) === "number") {
  142. logger.checkSafeUint53(value, "invalid hexlify value");
  143. let hex = "";
  144. while (value) {
  145. hex = HexCharacters[value & 0xf] + hex;
  146. value = Math.floor(value / 16);
  147. }
  148. if (hex.length) {
  149. if (hex.length % 2) {
  150. hex = "0" + hex;
  151. }
  152. return "0x" + hex;
  153. }
  154. return "0x00";
  155. }
  156. if (typeof (value) === "bigint") {
  157. value = value.toString(16);
  158. if (value.length % 2) {
  159. return ("0x0" + value);
  160. }
  161. return "0x" + value;
  162. }
  163. if (options.allowMissingPrefix && typeof (value) === "string" && value.substring(0, 2) !== "0x") {
  164. value = "0x" + value;
  165. }
  166. if (isHexable(value)) {
  167. return value.toHexString();
  168. }
  169. if (isHexString(value)) {
  170. if (value.length % 2) {
  171. if (options.hexPad === "left") {
  172. value = "0x0" + value.substring(2);
  173. }
  174. else if (options.hexPad === "right") {
  175. value += "0";
  176. }
  177. else {
  178. logger.throwArgumentError("hex data is odd-length", "value", value);
  179. }
  180. }
  181. return value.toLowerCase();
  182. }
  183. if (isBytes(value)) {
  184. let result = "0x";
  185. for (let i = 0; i < value.length; i++) {
  186. let v = value[i];
  187. result += HexCharacters[(v & 0xf0) >> 4] + HexCharacters[v & 0x0f];
  188. }
  189. return result;
  190. }
  191. return logger.throwArgumentError("invalid hexlify value", "value", value);
  192. }
  193. /*
  194. function unoddify(value: BytesLike | Hexable | number): BytesLike | Hexable | number {
  195. if (typeof(value) === "string" && value.length % 2 && value.substring(0, 2) === "0x") {
  196. return "0x0" + value.substring(2);
  197. }
  198. return value;
  199. }
  200. */
  201. export function hexDataLength(data) {
  202. if (typeof (data) !== "string") {
  203. data = hexlify(data);
  204. }
  205. else if (!isHexString(data) || (data.length % 2)) {
  206. return null;
  207. }
  208. return (data.length - 2) / 2;
  209. }
  210. export function hexDataSlice(data, offset, endOffset) {
  211. if (typeof (data) !== "string") {
  212. data = hexlify(data);
  213. }
  214. else if (!isHexString(data) || (data.length % 2)) {
  215. logger.throwArgumentError("invalid hexData", "value", data);
  216. }
  217. offset = 2 + 2 * offset;
  218. if (endOffset != null) {
  219. return "0x" + data.substring(offset, 2 + 2 * endOffset);
  220. }
  221. return "0x" + data.substring(offset);
  222. }
  223. export function hexConcat(items) {
  224. let result = "0x";
  225. items.forEach((item) => {
  226. result += hexlify(item).substring(2);
  227. });
  228. return result;
  229. }
  230. export function hexValue(value) {
  231. const trimmed = hexStripZeros(hexlify(value, { hexPad: "left" }));
  232. if (trimmed === "0x") {
  233. return "0x0";
  234. }
  235. return trimmed;
  236. }
  237. export function hexStripZeros(value) {
  238. if (typeof (value) !== "string") {
  239. value = hexlify(value);
  240. }
  241. if (!isHexString(value)) {
  242. logger.throwArgumentError("invalid hex string", "value", value);
  243. }
  244. value = value.substring(2);
  245. let offset = 0;
  246. while (offset < value.length && value[offset] === "0") {
  247. offset++;
  248. }
  249. return "0x" + value.substring(offset);
  250. }
  251. export function hexZeroPad(value, length) {
  252. if (typeof (value) !== "string") {
  253. value = hexlify(value);
  254. }
  255. else if (!isHexString(value)) {
  256. logger.throwArgumentError("invalid hex string", "value", value);
  257. }
  258. if (value.length > 2 * length + 2) {
  259. logger.throwArgumentError("value out of range", "value", arguments[1]);
  260. }
  261. while (value.length < 2 * length + 2) {
  262. value = "0x0" + value.substring(2);
  263. }
  264. return value;
  265. }
  266. export function splitSignature(signature) {
  267. const result = {
  268. r: "0x",
  269. s: "0x",
  270. _vs: "0x",
  271. recoveryParam: 0,
  272. v: 0,
  273. yParityAndS: "0x",
  274. compact: "0x"
  275. };
  276. if (isBytesLike(signature)) {
  277. let bytes = arrayify(signature);
  278. // Get the r, s and v
  279. if (bytes.length === 64) {
  280. // EIP-2098; pull the v from the top bit of s and clear it
  281. result.v = 27 + (bytes[32] >> 7);
  282. bytes[32] &= 0x7f;
  283. result.r = hexlify(bytes.slice(0, 32));
  284. result.s = hexlify(bytes.slice(32, 64));
  285. }
  286. else if (bytes.length === 65) {
  287. result.r = hexlify(bytes.slice(0, 32));
  288. result.s = hexlify(bytes.slice(32, 64));
  289. result.v = bytes[64];
  290. }
  291. else {
  292. logger.throwArgumentError("invalid signature string", "signature", signature);
  293. }
  294. // Allow a recid to be used as the v
  295. if (result.v < 27) {
  296. if (result.v === 0 || result.v === 1) {
  297. result.v += 27;
  298. }
  299. else {
  300. logger.throwArgumentError("signature invalid v byte", "signature", signature);
  301. }
  302. }
  303. // Compute recoveryParam from v
  304. result.recoveryParam = 1 - (result.v % 2);
  305. // Compute _vs from recoveryParam and s
  306. if (result.recoveryParam) {
  307. bytes[32] |= 0x80;
  308. }
  309. result._vs = hexlify(bytes.slice(32, 64));
  310. }
  311. else {
  312. result.r = signature.r;
  313. result.s = signature.s;
  314. result.v = signature.v;
  315. result.recoveryParam = signature.recoveryParam;
  316. result._vs = signature._vs;
  317. // If the _vs is available, use it to populate missing s, v and recoveryParam
  318. // and verify non-missing s, v and recoveryParam
  319. if (result._vs != null) {
  320. const vs = zeroPad(arrayify(result._vs), 32);
  321. result._vs = hexlify(vs);
  322. // Set or check the recid
  323. const recoveryParam = ((vs[0] >= 128) ? 1 : 0);
  324. if (result.recoveryParam == null) {
  325. result.recoveryParam = recoveryParam;
  326. }
  327. else if (result.recoveryParam !== recoveryParam) {
  328. logger.throwArgumentError("signature recoveryParam mismatch _vs", "signature", signature);
  329. }
  330. // Set or check the s
  331. vs[0] &= 0x7f;
  332. const s = hexlify(vs);
  333. if (result.s == null) {
  334. result.s = s;
  335. }
  336. else if (result.s !== s) {
  337. logger.throwArgumentError("signature v mismatch _vs", "signature", signature);
  338. }
  339. }
  340. // Use recid and v to populate each other
  341. if (result.recoveryParam == null) {
  342. if (result.v == null) {
  343. logger.throwArgumentError("signature missing v and recoveryParam", "signature", signature);
  344. }
  345. else if (result.v === 0 || result.v === 1) {
  346. result.recoveryParam = result.v;
  347. }
  348. else {
  349. result.recoveryParam = 1 - (result.v % 2);
  350. }
  351. }
  352. else {
  353. if (result.v == null) {
  354. result.v = 27 + result.recoveryParam;
  355. }
  356. else {
  357. const recId = (result.v === 0 || result.v === 1) ? result.v : (1 - (result.v % 2));
  358. if (result.recoveryParam !== recId) {
  359. logger.throwArgumentError("signature recoveryParam mismatch v", "signature", signature);
  360. }
  361. }
  362. }
  363. if (result.r == null || !isHexString(result.r)) {
  364. logger.throwArgumentError("signature missing or invalid r", "signature", signature);
  365. }
  366. else {
  367. result.r = hexZeroPad(result.r, 32);
  368. }
  369. if (result.s == null || !isHexString(result.s)) {
  370. logger.throwArgumentError("signature missing or invalid s", "signature", signature);
  371. }
  372. else {
  373. result.s = hexZeroPad(result.s, 32);
  374. }
  375. const vs = arrayify(result.s);
  376. if (vs[0] >= 128) {
  377. logger.throwArgumentError("signature s out of range", "signature", signature);
  378. }
  379. if (result.recoveryParam) {
  380. vs[0] |= 0x80;
  381. }
  382. const _vs = hexlify(vs);
  383. if (result._vs) {
  384. if (!isHexString(result._vs)) {
  385. logger.throwArgumentError("signature invalid _vs", "signature", signature);
  386. }
  387. result._vs = hexZeroPad(result._vs, 32);
  388. }
  389. // Set or check the _vs
  390. if (result._vs == null) {
  391. result._vs = _vs;
  392. }
  393. else if (result._vs !== _vs) {
  394. logger.throwArgumentError("signature _vs mismatch v and s", "signature", signature);
  395. }
  396. }
  397. result.yParityAndS = result._vs;
  398. result.compact = result.r + result.yParityAndS.substring(2);
  399. return result;
  400. }
  401. export function joinSignature(signature) {
  402. signature = splitSignature(signature);
  403. return hexlify(concat([
  404. signature.r,
  405. signature.s,
  406. (signature.recoveryParam ? "0x1c" : "0x1b")
  407. ]));
  408. }
  409. //# sourceMappingURL=index.js.map