@ledgerhq/coin-aptos
Version:
Ledger Aptos Coin integration
138 lines • 6.31 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IconGenerator = void 0;
// Utility for Xorshift-based PRNG
// Random number generator using Xorshift algorithm, as established in the Ethereum Blockies
// https://github.com/ethereum/blockies
class XorshiftPRNG {
state = [0, 0, 0, 0];
seed(seedStr) {
this.state.fill(0);
for (let i = 0; i < seedStr.length; i++) {
this.state[i % 4] = (this.state[i % 4] << 5) - this.state[i % 4] + seedStr.charCodeAt(i);
}
}
random() {
const t = this.state[0] ^ (this.state[0] << 11);
this.state[0] = this.state[1];
this.state[1] = this.state[2];
this.state[2] = this.state[3];
this.state[3] = this.state[3] ^ (this.state[3] >> 19) ^ t ^ (t >> 8);
return (this.state[3] >>> 0) / ((1 << 31) >>> 0);
}
}
// Identicon generator class
// Generates a unique identicon based on a seed string, and returns it as a BMP image data URL.
class IconGenerator {
rng;
constructor(seed) {
this.rng = new XorshiftPRNG();
this.rng.seed(seed);
}
random = () => this.rng.random();
// Generates a random color in RGB format
// The color is generated using HSL values, ensuring a good distribution of colors.
// The hue is randomized, saturation is between 40% and 100%, and lightness is a combination of random values.
// The resulting RGB values are rounded to integers between 0 and 255.
createColor() {
let h = Math.floor(this.random() * 360);
let s = this.random() * 60 + 40;
let l = (this.random() + this.random() + this.random() + this.random()) * 25;
h = ((h % 360) + 360) % 360;
s /= 100;
l /= 100;
const hueToRgb = (p, q, t) => {
if (t < 0)
t += 1;
if (t > 1)
t -= 1;
if (t < 1 / 6)
return p + (q - p) * 6 * t;
if (t < 1 / 2)
return q;
if (t < 2 / 3)
return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
if (s === 0)
return [l, l, l].map(v => Math.round(v * 255));
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
const r = hueToRgb(p, q, h / 360 + 1 / 3);
const g = hueToRgb(p, q, h / 360);
const b = hueToRgb(p, q, h / 360 - 1 / 3);
return [r, g, b].map(v => Math.round(v * 255));
}
// Creates a symmetric image data array for the identicon
// The image is symmetric, with the left half being a mirror of the right half.
// The data is generated by filling the left half with random values (0, 1, or 2),
// and mirroring it to the right half.
createImageData(size) {
const width = size;
const dataWidth = Math.ceil(width / 2);
const mirrorWidth = width - dataWidth;
const data = [];
for (let y = 0; y < size; y++) {
const row = Array.from({ length: dataWidth }, () => Math.floor(this.random() * 2.3));
const mirrored = row.slice(0, mirrorWidth).reverse();
row.push(...mirrored);
data.push(...row);
}
return data;
}
// Converts the image data into pixel data for BMP format
// The pixel data is generated by scaling the image data according to the specified scale factor.
// Each cell in the image data is represented by a block of pixels in the output,
// with the color determined by the value in the cell (0 for background, 1 for main color, 2 for spot color).
toPixels(size, cellData, scale, color, spotColor, bgColor) {
const rowSize = size * scale;
const rowDataSize = rowSize * 3;
const data = new Array(rowSize * rowDataSize);
for (let x = 0; x < size; x++) {
for (let y = 0; y < size; y++) {
const value = cellData[(size - x - 1) * size + y];
const c = value === 0 ? bgColor : value === 1 ? color : spotColor;
for (let dx = 0; dx < scale; dx++) {
for (let dy = 0; dy < scale; dy++) {
const pixelIndex = (x * scale + dx) * rowDataSize + (y * scale + dy) * 3;
[2, 1, 0].forEach((offset, idx) => {
data[pixelIndex + offset] = c[idx];
});
}
}
}
}
return data;
}
// Converts a number to little-endian hexadecimal format
static toLEHex(n) {
return (n + 2 ** 32).toString(16).match(/\B../g)?.reverse().join("") ?? "";
}
// Generates a BMP image data URL from pixel data
// The BMP format is constructed with a header and pixel data correctly padded.
// The header includes metadata such as size, width, height, and color depth.
static generateBMP(width, pixels) {
const height = Math.floor(pixels.length / (width * 3));
const size = this.toLEHex(26 + pixels.length);
const wh = this.toLEHex(width).slice(0, 4) + this.toLEHex(height).slice(0, 4);
const headerHex = `424d${size}000000001b0000000C000000${wh}0100180000`;
const headerBytes = headerHex.match(/../g)?.map(h => parseInt(h, 16)) ?? [];
const pixelChars = pixels.map(p => String.fromCharCode(p));
const base64Header = btoa(String.fromCharCode(...headerBytes));
const base64Pixels = btoa(pixelChars.join(""));
return `data:image/bmp;base64,${base64Header}${base64Pixels}`;
}
// Generates a BMP image data URL for the identicon
// The size and scale can be adjusted, with a default size of 8 and scale of 4.
// The generated image will have a symmetric pattern based on the seed string.
generate(size = 8, scale = 4) {
const mainColor = this.createColor();
const backgroundColor = this.createColor();
const spotColor = this.createColor();
const cellData = this.createImageData(size);
const pixelData = this.toPixels(size, cellData, scale, mainColor, spotColor, backgroundColor);
return IconGenerator.generateBMP(size * scale, pixelData);
}
}
exports.IconGenerator = IconGenerator;
//# sourceMappingURL=IconGenerator.js.map