@swaptoshi/dex-module
Version:
Klayr decentralized exchange (dex) on-chain module
308 lines • 14.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateSVG = generateSVG;
exports.rangeLocation = rangeLocation;
exports.isRare = isRare;
const crypto_1 = require("crypto");
const int_1 = require("../int");
const BitMath = require("../core/bit_math");
const curve1 = 'M1 1C41 41 105 105 145 145';
const curve2 = 'M1 1C33 49 97 113 145 145';
const curve3 = 'M1 1C33 57 89 113 145 145';
const curve4 = 'M1 1C25 65 81 121 145 145';
const curve5 = 'M1 1C17 73 73 129 145 145';
const curve6 = 'M1 1C9 81 65 137 145 145';
const curve7 = 'M1 1C1 89 57.5 145 145 145';
const curve8 = 'M1 1C1 97 49 145 145 145';
function generateSVG(params) {
return [
generateSVGDefs(params),
generateSVGBorderText(params.quoteToken, params.baseToken, params.quoteTokenSymbol, params.baseTokenSymbol),
generateSVGCardMantle(params.quoteTokenSymbol, params.baseTokenSymbol, params.feeTier),
generageSvgCurve(params.tickLower, params.tickUpper, params.tickSpacing, params.overRange),
generateSVGPositionDataAndLocationCurve(params.tokenId.toString(), params.tickLower, params.tickUpper),
generateSVGRareSparkle(params.tokenId, params.poolAddress),
'</svg>',
].join('');
}
function generateSVGDefs(params) {
const svg = [
'<svg width="290" height="500" viewBox="0 0 290 500" xmlns="http://www.w3.org/2000/svg"',
" xmlns:xlink='http://www.w3.org/1999/xlink'>",
'<defs>',
'<filter id="f1"><feImage result="p0" xlink:href="data:image/svg+xml;base64,',
Buffer.from(["<svg width='290' height='500' viewBox='0 0 290 500' xmlns='http://www.w3.org/2000/svg'><rect width='290px' height='500px' fill='#", params.color0, "'/></svg>"].join('')).toString('base64'),
'"/><feImage result="p1" xlink:href="data:image/svg+xml;base64,',
Buffer.from([
"<svg width='290' height='500' viewBox='0 0 290 500' xmlns='http://www.w3.org/2000/svg'><circle cx='",
params.x1,
"' cy='",
params.y1,
"' r='120px' fill='#",
params.color1,
"'/></svg>",
].join('')).toString('base64'),
'"/><feImage result="p2" xlink:href="data:image/svg+xml;base64,',
Buffer.from([
"<svg width='290' height='500' viewBox='0 0 290 500' xmlns='http://www.w3.org/2000/svg'><circle cx='",
params.x2,
"' cy='",
params.y2,
"' r='120px' fill='#",
params.color2,
"'/></svg>",
].join('')).toString('base64'),
'" />',
'<feImage result="p3" xlink:href="data:image/svg+xml;base64,',
Buffer.from([
"<svg width='290' height='500' viewBox='0 0 290 500' xmlns='http://www.w3.org/2000/svg'><circle cx='",
params.x3,
"' cy='",
params.y3,
"' r='100px' fill='#",
params.color3,
"'/></svg>",
].join('')).toString('base64'),
'" /><feBlend mode="overlay" in="p0" in2="p1" /><feBlend mode="exclusion" in2="p2" /><feBlend mode="overlay" in2="p3" result="blendOut" /><feGaussianBlur ',
'in="blendOut" stdDeviation="42" /></filter> <clipPath id="corners"><rect width="290" height="500" rx="42" ry="42" /></clipPath>',
'<path id="text-path-a" d="M40 12 H250 A28 28 0 0 1 278 40 V460 A28 28 0 0 1 250 488 H40 A28 28 0 0 1 12 460 V40 A28 28 0 0 1 40 12 z" />',
'<path id="minimap" d="M234 444C234 457.949 242.21 463 253 463" />',
'<filter id="top-region-blur"><feGaussianBlur in="SourceGraphic" stdDeviation="24" /></filter>',
'<linearGradient id="grad-up" x1="1" x2="0" y1="1" y2="0"><stop offset="0.0" stop-color="white" stop-opacity="1" />',
'<stop offset=".9" stop-color="white" stop-opacity="0" /></linearGradient>',
'<linearGradient id="grad-down" x1="0" x2="1" y1="0" y2="1"><stop offset="0.0" stop-color="white" stop-opacity="1" /><stop offset="0.9" stop-color="white" stop-opacity="0" /></linearGradient>',
'<mask id="fade-up" maskContentUnits="objectBoundingBox"><rect width="1" height="1" fill="url(#grad-up)" /></mask>',
'<mask id="fade-down" maskContentUnits="objectBoundingBox"><rect width="1" height="1" fill="url(#grad-down)" /></mask>',
'<mask id="none" maskContentUnits="objectBoundingBox"><rect width="1" height="1" fill="white" /></mask>',
'<linearGradient id="grad-symbol"><stop offset="0.7" stop-color="white" stop-opacity="1" /><stop offset=".95" stop-color="white" stop-opacity="0" /></linearGradient>',
'<mask id="fade-symbol" maskContentUnits="userSpaceOnUse"><rect width="290px" height="200px" fill="url(#grad-symbol)" /></mask></defs>',
'<g clip-path="url(#corners)">',
'<rect fill="',
params.color0,
'" x="0px" y="0px" width="290px" height="500px" />',
'<rect style="filter: url(#f1)" x="0px" y="0px" width="290px" height="500px" />',
' <g style="filter:url(#top-region-blur); transform:scale(1.5); transform-origin:center top;">',
'<rect fill="none" x="0px" y="0px" width="290px" height="500px" />',
'<ellipse cx="50%" cy="0px" rx="180px" ry="120px" fill="#000" opacity="0.85" /></g>',
'<rect x="0" y="0" width="290" height="500" rx="42" ry="42" fill="rgba(0,0,0,0)" stroke="rgba(255,255,255,0.2)" /></g>',
].join('');
return svg;
}
function generateSVGBorderText(quoteToken, baseToken, quoteTokenSymbol, baseTokenSymbol) {
const svg = [
'<text text-rendering="optimizeSpeed">',
'<textPath startOffset="-100%" fill="white" font-family="\'Courier New\', monospace" font-size="10px" xlink:href="#text-path-a">',
baseToken,
' • ',
baseTokenSymbol,
' <animate additive="sum" attributeName="startOffset" from="0%" to="100%" begin="0s" dur="30s" repeatCount="indefinite" />',
'</textPath> <textPath startOffset="0%" fill="white" font-family="\'Courier New\', monospace" font-size="10px" xlink:href="#text-path-a">',
baseToken,
' • ',
baseTokenSymbol,
' <animate additive="sum" attributeName="startOffset" from="0%" to="100%" begin="0s" dur="30s" repeatCount="indefinite" /> </textPath>',
'<textPath startOffset="50%" fill="white" font-family="\'Courier New\', monospace" font-size="10px" xlink:href="#text-path-a">',
quoteToken,
' • ',
quoteTokenSymbol,
' <animate additive="sum" attributeName="startOffset" from="0%" to="100%" begin="0s" dur="30s"',
' repeatCount="indefinite" /></textPath><textPath startOffset="-50%" fill="white" font-family="\'Courier New\', monospace" font-size="10px" xlink:href="#text-path-a">',
quoteToken,
' • ',
quoteTokenSymbol,
' <animate additive="sum" attributeName="startOffset" from="0%" to="100%" begin="0s" dur="30s" repeatCount="indefinite" /></textPath></text>',
].join('');
return svg;
}
function generateSVGCardMantle(quoteTokenSymbol, baseTokenSymbol, feeTier) {
const svg = [
'<g mask="url(#fade-symbol)"><rect fill="none" x="0px" y="0px" width="290px" height="200px" /> <text y="70px" x="32px" fill="white" font-family="\'Courier New\', monospace" font-weight="200" font-size="36px">',
quoteTokenSymbol,
'/',
baseTokenSymbol,
'</text><text y="115px" x="32px" fill="white" font-family="\'Courier New\', monospace" font-weight="200" font-size="36px">',
feeTier,
'</text></g>',
'<rect x="16" y="16" width="258" height="468" rx="26" ry="26" fill="rgba(0,0,0,0)" stroke="rgba(255,255,255,0.2)" />',
].join('');
return svg;
}
function generageSvgCurve(tickLower, tickUpper, tickSpacing, overRange) {
const fade = parseInt(overRange, 10) === 1 ? '#fade-up' : parseInt(overRange, 10) === -1 ? '#fade-down' : '#none';
const curve = getCurve(tickLower, tickUpper, tickSpacing);
const svg = [
'<g mask="url(',
fade,
')"',
' style="transform:translate(72px,189px)">',
'<rect x="-16px" y="-16px" width="180px" height="180px" fill="none" />',
'<path d="',
curve,
'" stroke="rgba(0,0,0,0.3)" stroke-width="32px" fill="none" stroke-linecap="round" />',
'</g><g mask="url(',
fade,
')"',
' style="transform:translate(72px,189px)">',
'<rect x="-16px" y="-16px" width="180px" height="180px" fill="none" />',
'<path d="',
curve,
'" stroke="rgba(255,255,255,1)" fill="none" stroke-linecap="round" /></g>',
generateSVGCurveCircle(overRange),
].join('');
return svg;
}
function getCurve(tickLower, tickUpper, tickSpacing) {
const tickRange = (parseInt(tickUpper, 10) - parseInt(tickLower, 10)) / parseInt(tickSpacing, 10);
let curve;
if (tickRange <= 4) {
curve = curve1;
}
else if (tickRange <= 8) {
curve = curve2;
}
else if (tickRange <= 16) {
curve = curve3;
}
else if (tickRange <= 32) {
curve = curve4;
}
else if (tickRange <= 64) {
curve = curve5;
}
else if (tickRange <= 128) {
curve = curve6;
}
else if (tickRange <= 256) {
curve = curve7;
}
else {
curve = curve8;
}
return curve;
}
function generateSVGCurveCircle(overRange) {
const curvex1 = '73';
const curvey1 = '190';
const curvex2 = '217';
const curvey2 = '334';
let svg;
if (parseInt(overRange, 10) === 1 || parseInt(overRange, 10) === -1) {
svg = [
'<circle cx="',
parseInt(overRange, 10) === -1 ? curvex1 : curvex2,
'px" cy="',
parseInt(overRange, 10) === -1 ? curvey1 : curvey2,
'px" r="4px" fill="white" /><circle cx="',
parseInt(overRange, 10) === -1 ? curvex1 : curvex2,
'px" cy="',
parseInt(overRange, 10) === -1 ? curvey1 : curvey2,
'px" r="24px" fill="none" stroke="white" />',
].join('');
}
else {
svg = ['<circle cx="', curvex1, 'px" cy="', curvey1, 'px" r="4px" fill="white" />', '<circle cx="', curvex2, 'px" cy="', curvey2, 'px" r="4px" fill="white" />'].join('');
}
return svg;
}
function generateSVGPositionDataAndLocationCurve(tokenId, tickLower, tickUpper) {
const tickLowerStr = tickToString(tickLower);
const tickUpperStr = tickToString(tickUpper);
const str1length = tokenId.length + 4;
const str2length = tickLowerStr.length + 10;
const str3length = tickUpperStr.length + 10;
const [xCoord, yCoord] = rangeLocation(tickLower, tickUpper);
const svg = [
' <g style="transform:translate(29px, 384px)">',
'<rect width="',
(7 * (str1length + 4)).toString(),
'px" height="26px" rx="8px" ry="8px" fill="rgba(0,0,0,0.6)" />',
'<text x="12px" y="17px" font-family="\'Courier New\', monospace" font-size="12px" fill="white"><tspan fill="rgba(255,255,255,0.6)">ID: </tspan>',
tokenId,
'</text></g>',
' <g style="transform:translate(29px, 414px)">',
'<rect width="',
(7 * (str2length + 4)).toString(),
'px" height="26px" rx="8px" ry="8px" fill="rgba(0,0,0,0.6)" />',
'<text x="12px" y="17px" font-family="\'Courier New\', monospace" font-size="12px" fill="white"><tspan fill="rgba(255,255,255,0.6)">Min Tick: </tspan>',
tickLowerStr,
'</text></g>',
' <g style="transform:translate(29px, 444px)">',
'<rect width="',
(7 * (str3length + 4)).toString(),
'px" height="26px" rx="8px" ry="8px" fill="rgba(0,0,0,0.6)" />',
'<text x="12px" y="17px" font-family="\'Courier New\', monospace" font-size="12px" fill="white"><tspan fill="rgba(255,255,255,0.6)">Max Tick: </tspan>',
tickUpperStr,
'</text></g>',
'<g style="transform:translate(226px, 433px)">',
'<rect width="36px" height="36px" rx="8px" ry="8px" fill="none" stroke="rgba(255,255,255,0.2)" />',
'<path stroke-linecap="round" d="M8 9C8.00004 22.9494 16.2099 28 27 28" fill="none" stroke="white" />',
'<circle style="transform:translate3d(',
xCoord,
'px, ',
yCoord,
'px, 0px)" cx="0px" cy="0px" r="4px" fill="white"/></g>',
].join('');
return svg;
}
function tickToString(tick) {
let sign = '';
if (int_1.Int24.from(tick).lt(0)) {
tick = int_1.Int24.from(tick).mul(-1).toString();
sign = '-';
}
return [sign, tick.toString()].join('');
}
function rangeLocation(tickLower, tickUpper) {
const midPoint = (parseInt(tickLower, 10) + parseInt(tickUpper, 10)) / 2;
if (midPoint < -125000) {
return ['8', '7'];
}
if (midPoint < -75000) {
return ['8', '10.5'];
}
if (midPoint < -25000) {
return ['8', '14.25'];
}
if (midPoint < -5000) {
return ['10', '18'];
}
if (midPoint < 0) {
return ['11', '21'];
}
if (midPoint < 5000) {
return ['13', '23'];
}
if (midPoint < 25000) {
return ['15', '25'];
}
if (midPoint < 75000) {
return ['18', '26'];
}
if (midPoint < 125000) {
return ['21', '27'];
}
return ['24', '27'];
}
function generateSVGRareSparkle(tokenId, poolAddress) {
let svg;
if (isRare(tokenId, poolAddress)) {
svg = [
'<g style="transform:translate(226px, 392px)"><rect width="36px" height="36px" rx="8px" ry="8px" fill="none" stroke="rgba(255,255,255,0.2)" />',
'<g><path style="transform:translate(6px,6px)" d="M12 0L12.6522 9.56587L18 1.6077L13.7819 10.2181L22.3923 6L14.4341 ',
'11.3478L24 12L14.4341 12.6522L22.3923 18L13.7819 13.7819L18 22.3923L12.6522 14.4341L12 24L11.3478 14.4341L6 22.39',
'23L10.2181 13.7819L1.6077 18L9.56587 12.6522L0 12L9.56587 11.3478L1.6077 6L10.2181 10.2181L6 1.6077L11.3478 9.56587L12 0Z" fill="white" />',
'<animateTransform attributeName="transform" type="rotate" from="0 18 18" to="360 18 18" dur="10s" repeatCount="indefinite"/></g></g>',
].join('');
}
else {
svg = '';
}
return svg;
}
function isRare(tokenId, poolAddress) {
const h = `0x${(0, crypto_1.createHash)('sha256')
.update(tokenId + poolAddress.toString('hex'))
.digest('hex')}`;
return int_1.Uint256.from(h).lt(int_1.Uint256.from(int_1.Uint256.MAX).div(1 + parseInt(BitMath.mostSignificantBit(int_1.Uint64.from(tokenId).add(1).toString()), 10) * 2));
}
//# sourceMappingURL=nft_svg.js.map