From 0d457132de6bd34739f7b919ebb4d3be7e489099 Mon Sep 17 00:00:00 2001 From: Oighty Date: Mon, 17 Jun 2024 16:41:43 -0500 Subject: [PATCH 01/14] wip: LIV svg image --- src/lib/SVG.sol | 327 ++++++ src/modules/derivatives/DerivativeCard.sol | 372 +++++++ utils/hot-chain-svg/package.json | 25 + utils/hot-chain-svg/src/boot.js | 22 + utils/hot-chain-svg/src/call.js | 34 + utils/hot-chain-svg/src/compile.js | 69 ++ utils/hot-chain-svg/src/deploy.js | 27 + utils/hot-chain-svg/src/index.js | 29 + utils/hot-chain-svg/src/qa.js | 35 + utils/hot-chain-svg/src/serve.js | 48 + utils/hot-chain-svg/src/sync-nfts.js | 66 ++ utils/hot-chain-svg/yarn.lock | 1167 ++++++++++++++++++++ 12 files changed, 2221 insertions(+) create mode 100644 src/lib/SVG.sol create mode 100644 src/modules/derivatives/DerivativeCard.sol create mode 100644 utils/hot-chain-svg/package.json create mode 100644 utils/hot-chain-svg/src/boot.js create mode 100644 utils/hot-chain-svg/src/call.js create mode 100644 utils/hot-chain-svg/src/compile.js create mode 100644 utils/hot-chain-svg/src/deploy.js create mode 100644 utils/hot-chain-svg/src/index.js create mode 100644 utils/hot-chain-svg/src/qa.js create mode 100644 utils/hot-chain-svg/src/serve.js create mode 100644 utils/hot-chain-svg/src/sync-nfts.js create mode 100644 utils/hot-chain-svg/yarn.lock diff --git a/src/lib/SVG.sol b/src/lib/SVG.sol new file mode 100644 index 00000000..98054ba3 --- /dev/null +++ b/src/lib/SVG.sol @@ -0,0 +1,327 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.12; + +// hot-chain-svg: https://github.com/w1nt3r-eth/hot-chain-svg + +// Core utils used extensively to format CSS and numbers. +library utils { + // used to simulate empty strings + string internal constant NULL = ''; + + // formats a CSS variable line. includes a semicolon for formatting. + function setCssVar(string memory _key, string memory _val) + internal + pure + returns (string memory) + { + return string.concat('--', _key, ':', _val, ';'); + } + + // formats getting a css variable + function getCssVar(string memory _key) + internal + pure + returns (string memory) + { + return string.concat('var(--', _key, ')'); + } + + // formats getting a def URL + function getDefURL(string memory _id) + internal + pure + returns (string memory) + { + return string.concat('url(#', _id, ')'); + } + + // formats rgba white with a specified opacity / alpha + function white_a(uint256 _a) internal pure returns (string memory) { + return rgba(255, 255, 255, _a); + } + + // formats rgba black with a specified opacity / alpha + function black_a(uint256 _a) internal pure returns (string memory) { + return rgba(0, 0, 0, _a); + } + + // formats generic rgba color in css + function rgba( + uint256 _r, + uint256 _g, + uint256 _b, + uint256 _a + ) internal pure returns (string memory) { + string memory formattedA = _a < 100 + ? string.concat('0.', utils.uint2str(_a)) + : '1'; + return + string.concat( + 'rgba(', + utils.uint2str(_r), + ',', + utils.uint2str(_g), + ',', + utils.uint2str(_b), + ',', + formattedA, + ')' + ); + } + + // checks if two strings are equal + function stringsEqual(string memory _a, string memory _b) + internal + pure + returns (bool) + { + return + keccak256(abi.encodePacked(_a)) == keccak256(abi.encodePacked(_b)); + } + + // returns the length of a string in characters + function utfStringLength(string memory _str) + internal + pure + returns (uint256 length) + { + uint256 i = 0; + bytes memory string_rep = bytes(_str); + + while (i < string_rep.length) { + if (string_rep[i] >> 7 == 0) i += 1; + else if (string_rep[i] >> 5 == bytes1(uint8(0x6))) i += 2; + else if (string_rep[i] >> 4 == bytes1(uint8(0xE))) i += 3; + else if (string_rep[i] >> 3 == bytes1(uint8(0x1E))) + i += 4; + //For safety + else i += 1; + + length++; + } + } + + // converts an unsigned integer to a string + function uint2str(uint256 _i) + internal + pure + returns (string memory _uintAsString) + { + if (_i == 0) { + return '0'; + } + uint256 j = _i; + uint256 len; + while (j != 0) { + len++; + j /= 10; + } + bytes memory bstr = new bytes(len); + uint256 k = len; + while (_i != 0) { + k = k - 1; + uint8 temp = (48 + uint8(_i - (_i / 10) * 10)); + bytes1 b1 = bytes1(temp); + bstr[k] = b1; + _i /= 10; + } + return string(bstr); + } +} + +// Core SVG utilitiy library which helps us construct +// onchain SVG's with a simple, web-like API. +library svg { + /* MAIN ELEMENTS */ + function g(string memory _props, string memory _children) + internal + pure + returns (string memory) + { + return el('g', _props, _children); + } + + function path(string memory _props, string memory _children) + internal + pure + returns (string memory) + { + return el('path', _props, _children); + } + + function text(string memory _props, string memory _children) + internal + pure + returns (string memory) + { + return el('text', _props, _children); + } + + function line(string memory _props, string memory _children) + internal + pure + returns (string memory) + { + return el('line', _props, _children); + } + + function circle(string memory _props, string memory _children) + internal + pure + returns (string memory) + { + return el('circle', _props, _children); + } + + function circle(string memory _props) + internal + pure + returns (string memory) + { + return el('circle', _props); + } + + function ellipse(string memory _props, string memory _children) + internal + pure + returns (string memory) + { + return el('ellipse', _props, _children); + } + + function ellipse(string memory _props) internal pure returns (string memory) { + return el('ellipse', _props); + } + + function rect(string memory _props, string memory _children) + internal + pure + returns (string memory) + { + return el('rect', _props, _children); + } + + function rect(string memory _props) + internal + pure + returns (string memory) + { + return el('rect', _props); + } + + function filter(string memory _props, string memory _children) + internal + pure + returns (string memory) + { + return el('filter', _props, _children); + } + + function cdata(string memory _content) + internal + pure + returns (string memory) + { + return string.concat(''); + } + + /* GRADIENTS */ + function radialGradient(string memory _props, string memory _children) + internal + pure + returns (string memory) + { + return el('radialGradient', _props, _children); + } + + function linearGradient(string memory _props, string memory _children) + internal + pure + returns (string memory) + { + return el('linearGradient', _props, _children); + } + + function gradientStop( + uint256 offset, + string memory stopColor, + string memory _props + ) internal pure returns (string memory) { + return + el( + 'stop', + string.concat( + prop('stop-color', stopColor), + ' ', + prop('offset', string.concat(utils.uint2str(offset), '%')), + ' ', + _props + ) + ); + } + + function animateTransform(string memory _props) + internal + pure + returns (string memory) + { + return el('animateTransform', _props); + } + + function image(string memory _href, string memory _props) + internal + pure + returns (string memory) + { + return + el( + 'image', + string.concat(prop('href', _href), ' ', _props) + ); + } + + /* COMMON */ + // A generic element, can be used to construct any SVG (or HTML) element + function el( + string memory _tag, + string memory _props, + string memory _children + ) internal pure returns (string memory) { + return + string.concat( + '<', + _tag, + ' ', + _props, + '>', + _children, + '' + ); + } + + // A generic element, can be used to construct any SVG (or HTML) element without children + function el( + string memory _tag, + string memory _props + ) internal pure returns (string memory) { + return + string.concat( + '<', + _tag, + ' ', + _props, + '/>' + ); + } + + // an SVG attribute + function prop(string memory _key, string memory _val) + internal + pure + returns (string memory) + { + return string.concat(_key, '=', '"', _val, '" '); + } +} diff --git a/src/modules/derivatives/DerivativeCard.sol b/src/modules/derivatives/DerivativeCard.sol new file mode 100644 index 00000000..828a398e --- /dev/null +++ b/src/modules/derivatives/DerivativeCard.sol @@ -0,0 +1,372 @@ +/// SPDX-License-Identifier: BSL-1.1 +pragma solidity 0.8.19; + +import {svg, utils} from "src/lib/SVG.sol"; + +contract DerivativeCard { + + struct Info { + string derivativeType; + string baseAssetSymbol; + string quoteAssetSymbol; + Property[] properties; + } + + struct Property { + string key; + string value; + } + + struct Colors { + string blue; + string lightBlue; + string green; + string yellowGreen; + string yellow; + string orange; + string red; + } + + string internal constant STROKE = 'stroke="#000" stroke-miterlimit="10" stroke-width="4px" '; + Colors internal colors; + + constructor() { + colors = Colors({ + blue: 'rgb(110, 148, 240)', + lightBlue: 'rgb(118, 189, 242)', + green: 'rgb(206, 244, 117)', + yellowGreen: 'rgb(243, 244, 189)', + yellow: 'rgb(243, 244, 189)', + orange: 'rgb(246, 172, 84)', + red: 'rgb(242, 103, 64)' + }); + } + + function render(Info memory tokenInfo) internal view returns (string memory) { + return string.concat( + '', + svg.el('defs', utils.NULL, string.concat(fullGradient(), fullVertGradient(), blueGreenGradient(), orangeRedGradient())), + svg.rect( + string.concat( + svg.prop('x', '5'), + svg.prop('y', '5'), + svg.prop('width', '280'), + svg.prop('height', '490'), + svg.prop('fill', 'black'), + svg.prop('rx', '10'), + svg.prop('ry', '10') + ), + utils.NULL + ), + svg.text( + string.concat( + svg.prop('x', '145'), + svg.prop('y', '30'), + svg.prop('fill', 'white'), + svg.prop('font-size', '20'), + svg.prop('font-family', "\'Courier New\', monospace"), + svg.prop('text-anchor', 'middle') + ), + tokenInfo.derivativeType + ), + svg.text( + string.concat( + svg.prop('x', '145'), + svg.prop('y', '90'), + svg.prop('fill', 'white'), + svg.prop('font-size', '56'), + svg.prop('font-family', "\'Courier New\', monospace"), + svg.prop('text-anchor', 'middle') + ), + tokenInfo.baseAssetSymbol + ), + progressBar(100), + svg.g( + string.concat( + svg.prop('transform', 'translate(96, 200) scale(0.1)') + ), + logo() + ), + svg.text( + string.concat( + svg.prop('x', '20'), + svg.prop('y', '350'), + svg.prop('fill', 'white'), + svg.prop('font-size', '16'), + svg.prop('font-family', "\'Courier New\', monospace"), + svg.prop('text-anchor', 'left') + ), + string.concat( + tokenInfo.properties[0].key, + ': ', + tokenInfo.properties[0].value + ) + ), + svg.text( + string.concat( + svg.prop('x', '20'), + svg.prop('y', '380'), + svg.prop('fill', 'white'), + svg.prop('font-size', '16'), + svg.prop('font-family', "\'Courier New\', monospace"), + svg.prop('text-anchor', 'left') + ), + string.concat( + tokenInfo.properties[1].key, + ': ', + tokenInfo.properties[1].value + ) + ), + svg.g( + string.concat( + svg.prop('transform', 'translate(80, 430) scale(0.05)') + ), + wordmark() + ), + '' + ); + } + + function logo() internal pure returns (string memory) { + return svg.path( + string.concat( + svg.prop('fill' , "url('#fullVertGradient')"), + STROKE, + svg.prop('d', 'M0.34668 818.666L20.3467 852.666L474.347 590.666V838.666H513.347V590.666L966.347 852.666L986.347 818.666L532.347 556.666L746.347 433.666L726.347 399.666L513.347 522.666L514.347 0.665527H474.347V522.666L260.347 399.666L240.347 433.666L454.347 556.666L0.34668 818.666Z') + ), + utils.NULL + ); + } + + function wordmark() internal pure returns (string memory) { + return svg.path( + string.concat( + svg.prop('fill' , "url('#fullGradient')"), + STROKE, + svg.prop('d', 'M2078.65 610.666H2118.65C2118.65 720.666 2205.65 799.666 2327.65 799.666C2439.65 799.666 2528.65 738.666 2528.65 629.666C2528.65 526.666 2460.65 465.666 2318.65 428.666C2167.65 397.666 2090.65 318.666 2090.65 201.666C2090.65 84.6656 2204.65 -2.33439 2323.65 0.665612C2450.65 -2.33439 2559.65 90.6656 2559.65 223.666H2521.65C2521.65 112.666 2432.65 40.6656 2323.65 40.6656C2223.65 40.6656 2130.65 110.666 2130.65 201.666C2130.65 300.666 2200.65 360.666 2331.65 394.666C2492.65 431.666 2569.65 505.666 2569.65 624.666C2569.65 743.666 2461.65 839.666 2323.65 839.666C2185.65 839.666 2078.65 742.666 2078.65 610.666ZM1926.65 820.666V20.6656H1966.65V820.666H1926.65ZM1005.65 801.666L1035.65 831.666L1416.65 450.666L1797.65 830.666L1826.65 800.666L1445.65 419.666L1826.65 40.6656L1796.65 10.6656L1415.65 391.666L1034.65 11.6656L1005.65 41.6656L1386.65 419.666L1005.65 801.666ZM45.6533 419.666C45.6533 627.666 212.653 794.666 419.653 794.666C611.653 794.666 793.653 648.666 793.653 419.666C793.653 190.666 611.653 45.6656 419.653 45.6656C212.653 45.6656 45.6533 213.666 45.6533 419.666ZM0.65332 419.666C0.65332 188.666 188.653 0.665612 419.653 0.665612C582.653 0.665612 761.653 103.666 817.653 299.666L869.653 20.6656H910.653L833.653 419.666L910.653 820.666H869.653L817.653 540.666C761.653 736.666 582.653 839.666 419.653 839.666C188.653 839.666 0.65332 651.666 0.65332 419.666Z') + ), + utils.NULL + ); + } + + function fullGradient() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop('id', 'fullGradient') + ), + string.concat( + svg.gradientStop( + 2, + colors.blue, + utils.NULL + ), + svg.gradientStop( + 10, + colors.lightBlue, + utils.NULL + ), + svg.gradientStop( + 32, + colors.green, + utils.NULL + ), + svg.gradientStop( + 49, + colors.yellowGreen, + utils.NULL + ), + svg.gradientStop( + 52, + colors.yellow, + utils.NULL + ), + svg.gradientStop( + 79, + colors.orange, + utils.NULL + ), + svg.gradientStop( + 100, + colors.red, + utils.NULL + ) + ) + ); + } + + function fullVertGradient() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop('id', 'fullVertGradient'), + svg.prop('gradientTransform', 'rotate(90)') + ), + string.concat( + svg.gradientStop( + 8, + colors.blue, + utils.NULL + ), + svg.gradientStop( + 20, + colors.lightBlue, + utils.NULL + ), + svg.gradientStop( + 42, + colors.green, + utils.NULL + ), + svg.gradientStop( + 59, + colors.yellowGreen, + utils.NULL + ), + svg.gradientStop( + 65, + colors.yellow, + utils.NULL + ), + svg.gradientStop( + 79, + colors.orange, + utils.NULL + ), + svg.gradientStop( + 100, + colors.red, + utils.NULL + ) + ) + ); + } + + function blueGreenGradient() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop('id', 'blueGreenGradient'), + svg.prop('gradientUnits', 'userSpaceOnUse') + ), + string.concat( + svg.gradientStop( + 20, + colors.blue, + utils.NULL + ), + svg.gradientStop( + 40, + colors.lightBlue, + utils.NULL + ), + svg.gradientStop( + 60, + colors.green, + utils.NULL + ) + ) + ); + } + + function orangeRedGradient() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop('id', 'orangeRedGradient'), + svg.prop('gradientTransform', 'rotate(90)') + ), + string.concat( + svg.gradientStop( + 30, + colors.orange, + utils.NULL + ), + svg.gradientStop( + 90, + colors.red, + utils.NULL + ) + ) + ); + } + + function progressBar(uint256 _progress) internal view returns (string memory) { + + uint256 len = 168 * _progress / 100; + + string memory startBar = svg.line( + string.concat( + svg.prop('x1', '60'), + svg.prop('y1', '135'), + svg.prop('x2', '60'), + svg.prop('y2', '155'), + svg.prop('stroke', colors.blue), + svg.prop('stroke-width', '6') + ), + utils.NULL + ); + + string memory endBarColor = _progress == 100 ? colors.green : 'grey'; + + string memory endBar = svg.line( + string.concat( + svg.prop('x1', '230'), + svg.prop('y1', '135'), + svg.prop('x2', '230'), + svg.prop('y2', '155'), + svg.prop('stroke', endBarColor), + svg.prop('stroke-width', '6') + ), + utils.NULL + ); + + + string memory progressLine = svg.line( + string.concat( + svg.prop('x1', '63'), + svg.prop('y1', '145'), + svg.prop('x2', utils.uint2str(62 + len)), + svg.prop('y2', '145'), + svg.prop('stroke', "url('#blueGreenGradient')"), + svg.prop('stroke-width', '6') + ), + utils.NULL + ); + + string memory shadowLine = svg.line( + string.concat( + svg.prop('x1', '63'), + svg.prop('y1', '145'), + svg.prop('x2', '230'), + svg.prop('y2', '145'), + svg.prop('stroke', 'grey'), + svg.prop('stroke-width', '4') + ), + utils.NULL + ); + + return svg.g( + utils.NULL, + string.concat( + startBar, + shadowLine, + progressLine, + endBar + ) + ); + } + + function example() external view returns (string memory) { + Property[] memory properties = new Property[](2); + properties[0] = Property({key: "Vesting Start", value: "2024-07-01"}); + properties[1] = Property({key: "Vesting End", value: "2024-09-01"}); + + + Info memory info = Info({ + derivativeType: "Linear Vesting Token", + baseAssetSymbol: "XYZ", + quoteAssetSymbol: "", + properties: properties + }); + + return render(info); + } + +} \ No newline at end of file diff --git a/utils/hot-chain-svg/package.json b/utils/hot-chain-svg/package.json new file mode 100644 index 00000000..63697bb4 --- /dev/null +++ b/utils/hot-chain-svg/package.json @@ -0,0 +1,25 @@ +{ + "name": "hot-chain-svg", + "version": "0.0.1", + "main": "src/index.js", + "author": "w1nt3r.eth", + "license": "MIT", + "scripts": { + "start": "node .", + "qa": "node src/qa.js", + "sync-nfts": "node src/sync-nfts.js" + }, + "prettier": { + "singleQuote": true + }, + "dependencies": { + "@ethereumjs/vm": "^5.8.0", + "@ethersproject/abi": "^5.6.0", + "ethers": "^5.6.8", + "solc": "0.8.19", + "xmldom": "^0.6.0" + }, + "devDependencies": { + "prettier": "^2.6.2" + } +} diff --git a/utils/hot-chain-svg/src/boot.js b/utils/hot-chain-svg/src/boot.js new file mode 100644 index 00000000..1eeb4fff --- /dev/null +++ b/utils/hot-chain-svg/src/boot.js @@ -0,0 +1,22 @@ +const { Account, Address, BN } = require('ethereumjs-util'); +const VM = require('@ethereumjs/vm').default; + +async function boot() { + const pk = Buffer.from( + '1122334455667788112233445566778811223344556677881122334455667788', + 'hex' + ); + + const accountAddress = Address.fromPrivateKey(pk); + const account = Account.fromAccountData({ + nonce: 0, + balance: new BN(10).pow(new BN(18 + 2)), // 100 eth + }); + + const vm = new VM(); + await vm.stateManager.putAccount(accountAddress, account); + + return { vm, pk }; +} + +module.exports = boot; diff --git a/utils/hot-chain-svg/src/call.js b/utils/hot-chain-svg/src/call.js new file mode 100644 index 00000000..2a8e46cd --- /dev/null +++ b/utils/hot-chain-svg/src/call.js @@ -0,0 +1,34 @@ +const { defaultAbiCoder, Interface } = require('@ethersproject/abi'); + +async function call(vm, address, abi, name, args = []) { + const iface = new Interface(abi); + const data = iface.encodeFunctionData(name, args); + + const renderResult = await vm.runCall({ + to: address, + caller: address, + origin: address, + data: Buffer.from(data.slice(2), 'hex'), + }); + + if (renderResult.execResult.exceptionError) { + throw renderResult.execResult.exceptionError; + } + + const logs = renderResult.execResult.logs?.map(([address, topic, data]) => + data.toString().replace(/\x00/g, '') + ); + + if (logs?.length) { + console.log(logs); + } + + const results = defaultAbiCoder.decode( + ['string'], + renderResult.execResult.returnValue + ); + + return results[0]; +} + +module.exports = call; diff --git a/utils/hot-chain-svg/src/compile.js b/utils/hot-chain-svg/src/compile.js new file mode 100644 index 00000000..5077a8ad --- /dev/null +++ b/utils/hot-chain-svg/src/compile.js @@ -0,0 +1,69 @@ +const fs = require('fs'); +const path = require('path'); +const solc = require('solc'); + +function getSolcInput(source) { + return { + language: 'Solidity', + sources: { + [path.basename(source)]: { + content: fs.readFileSync(source, 'utf8'), + }, + }, + settings: { + optimizer: { + enabled: false, + runs: 1, + }, + evmVersion: 'london', + outputSelection: { + '*': { + '*': ['abi', 'evm.bytecode'], + }, + }, + }, + }; +} + +function findImports(path) { + try { + const file = fs.existsSync(path) + ? fs.readFileSync(path, 'utf8') + : fs.readFileSync(require.resolve(path), 'utf8'); + return { contents: file }; + } catch (error) { + console.error(error); + return { error }; + } +} + +function compile(source, project_dir) { + const input = getSolcInput(source); + process.chdir(project_dir); + const output = JSON.parse( + solc.compile(JSON.stringify(input), { import: findImports }) + ); + + let errors = []; + + if (output.errors) { + for (const error of output.errors) { + if (error.severity === 'error') { + errors.push(error.formattedMessage); + } + } + } + + if (errors.length > 0) { + throw new Error(errors.join('\n\n')); + } + + const result = output.contracts[path.basename(source)]; + const contractName = Object.keys(result)[0]; + return { + abi: result[contractName].abi, + bytecode: result[contractName].evm.bytecode.object, + }; +} + +module.exports = compile; diff --git a/utils/hot-chain-svg/src/deploy.js b/utils/hot-chain-svg/src/deploy.js new file mode 100644 index 00000000..b2d317b9 --- /dev/null +++ b/utils/hot-chain-svg/src/deploy.js @@ -0,0 +1,27 @@ +const { Address } = require('ethereumjs-util'); +const { Transaction } = require('@ethereumjs/tx'); + +async function deploy(vm, pk, bytecode) { + const address = Address.fromPrivateKey(pk); + const account = await vm.stateManager.getAccount(address); + + const txData = { + value: 0, + gasLimit: 200_000_000_000, + gasPrice: 1, + data: '0x' + bytecode.toString('hex'), + nonce: account.nonce, + }; + + const tx = Transaction.fromTxData(txData).sign(pk); + + const deploymentResult = await vm.runTx({ tx }); + + if (deploymentResult.execResult.exceptionError) { + throw deploymentResult.execResult.exceptionError; + } + + return deploymentResult.createdAddress; +} + +module.exports = deploy; diff --git a/utils/hot-chain-svg/src/index.js b/utils/hot-chain-svg/src/index.js new file mode 100644 index 00000000..4f71faea --- /dev/null +++ b/utils/hot-chain-svg/src/index.js @@ -0,0 +1,29 @@ +const fs = require('fs'); +const path = require('path'); +const serve = require('./serve'); +const boot = require('./boot'); +const call = require('./call'); +const compile = require('./compile'); +const deploy = require('./deploy'); + +const SOURCE = path.join(__dirname, '..', '..', '..', 'src', 'modules', 'derivatives', 'DerivativeCard.sol'); +const PROJECT_DIR = path.join(__dirname, '..', '..', '..'); + +async function main() { + const { vm, pk } = await boot(); + + async function handler() { + const { abi, bytecode } = compile(SOURCE, PROJECT_DIR); + const address = await deploy(vm, pk, bytecode); + const result = await call(vm, address, abi, 'example'); + return result; + } + + const { notify } = await serve(handler); + + fs.watch(path.dirname(SOURCE), notify); + console.log('Watching', path.dirname(SOURCE)); + console.log('Serving http://localhost:9901/'); +} + +main(); diff --git a/utils/hot-chain-svg/src/qa.js b/utils/hot-chain-svg/src/qa.js new file mode 100644 index 00000000..a967a4ee --- /dev/null +++ b/utils/hot-chain-svg/src/qa.js @@ -0,0 +1,35 @@ +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const boot = require('./boot'); +const call = require('./call'); +const compile = require('./compile'); +const deploy = require('./deploy'); +const { DOMParser } = require('xmldom'); + +const SOURCE = path.join(__dirname, '..', 'contracts', 'Renderer.sol'); +const DESTINATION = path.join(os.tmpdir(), 'hot-chain-svg-'); + +async function main() { + const { vm, pk } = await boot(); + const { abi, bytecode } = compile(SOURCE); + const address = await deploy(vm, pk, bytecode); + + const tempFolder = fs.mkdtempSync(DESTINATION); + console.log('Saving to', tempFolder); + + for (let i = 1; i < 256; i++) { + const fileName = path.join(tempFolder, i + '.svg'); + console.log('Rendering', fileName); + const svg = await call(vm, address, abi, 'render', [i]); + fs.writeFileSync(fileName, svg); + + // Throws on invalid XML + new DOMParser().parseFromString(svg); + } +} + +main().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/utils/hot-chain-svg/src/serve.js b/utils/hot-chain-svg/src/serve.js new file mode 100644 index 00000000..a370b377 --- /dev/null +++ b/utils/hot-chain-svg/src/serve.js @@ -0,0 +1,48 @@ +const http = require('http'); +const EventEmitter = require('events'); + +async function serve(handler) { + const events = new EventEmitter(); + + function requestListener(req, res) { + if (req.url === '/changes') { + res.setHeader('Content-Type', 'text/event-stream'); + res.writeHead(200); + const sendEvent = () => res.write('event: change\ndata:\n\n'); + events.on('change', sendEvent); + req.on('close', () => events.off('change', sendEvent)); + return; + } + + if (req.url === '/') { + res.writeHead(200); + handler().then( + (content) => res.end(webpage(content)), + (error) => res.end(webpage(`
${error.message}
`)) + ); + return; + } + + res.writeHead(404); + res.end('Not found: ' + req.url); + } + const server = http.createServer(requestListener); + await new Promise((resolve) => server.listen(9901, resolve)); + + return { + notify: () => events.emit('change'), + }; +} + +const webpage = (content) => ` + +Hot Chain SVG +${content} + + +`; + +module.exports = serve; diff --git a/utils/hot-chain-svg/src/sync-nfts.js b/utils/hot-chain-svg/src/sync-nfts.js new file mode 100644 index 00000000..52c4aff4 --- /dev/null +++ b/utils/hot-chain-svg/src/sync-nfts.js @@ -0,0 +1,66 @@ +const { formatEther } = require('@ethersproject/units'); +const ethers = require('ethers'); +const fs = require('fs'); +const path = require('path'); + +const address = '0xa7988c8abb7706e024a8f2a1328e376227aaad18'; +const url = process.env.PROVIDER_URL; + +async function main() { + const provider = new ethers.providers.JsonRpcProvider({ url }); + const abi = [ + 'event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 amount)', + 'event CollectionNameUpdated(address indexed collection, string name)', + ]; + const contract = new ethers.Contract(address, abi, provider); + + const [mints, names] = await Promise.all([ + contract.queryFilter( + contract.filters.TransferSingle(null, ethers.constants.AddressZero) + ), + contract.queryFilter(contract.filters.CollectionNameUpdated()), + ]); + + const collectionNames = Object.fromEntries( + names.map((e) => [e.args.collection.toLowerCase(), e.args.name]) + ); + + const owners = [...new Set(mints.map((e) => e.args.to.toLowerCase()))]; + const ensNames = await Promise.all( + owners.map((address) => provider.lookupAddress(address)) + ); + + const ownerNames = Object.fromEntries( + owners.map((address, index) => [address, ensNames[index] || address]) + ); + + const log = mints.map((e) => { + const { id, to } = e.args; + const collection = ethers.utils.hexZeroPad( + id.and( + ethers.BigNumber.from('0xffffffffffffffffffffffffffffffffffffffff') + ), + 20 + ); + + const name = collectionNames[collection] || collection; + const owner = ownerNames[to.toLowerCase()]; + + return `- [${name}](https://etherscan.io/address/${collection}) by ${owner}`; + }); + + console.log(log); + + const readme = path.resolve(__dirname, '..', 'README.md'); + const content = fs.readFileSync(readme, 'utf8'); + const updated = content.replaceAll( + /[\s\S]+/gim, + ['', ...log, ''].join('\n') + ); + fs.writeFileSync(readme, updated); +} + +main().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/utils/hot-chain-svg/yarn.lock b/utils/hot-chain-svg/yarn.lock new file mode 100644 index 00000000..0fe67215 --- /dev/null +++ b/utils/hot-chain-svg/yarn.lock @@ -0,0 +1,1167 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ethereumjs/block@^3.5.0", "@ethereumjs/block@^3.6.2", "@ethereumjs/block@^3.6.3": + version "3.6.3" + resolved "https://registry.yarnpkg.com/@ethereumjs/block/-/block-3.6.3.tgz#d96cbd7af38b92ebb3424223dbf773f5ccd27f84" + integrity sha512-CegDeryc2DVKnDkg5COQrE0bJfw/p0v3GBk2W5/Dj5dOVfEmb50Ux0GLnSPypooLnfqjwFaorGuT9FokWB3GRg== + dependencies: + "@ethereumjs/common" "^2.6.5" + "@ethereumjs/tx" "^3.5.2" + ethereumjs-util "^7.1.5" + merkle-patricia-tree "^4.2.4" + +"@ethereumjs/blockchain@^5.5.3": + version "5.5.3" + resolved "https://registry.yarnpkg.com/@ethereumjs/blockchain/-/blockchain-5.5.3.tgz#aa49a6a04789da6b66b5bcbb0d0b98efc369f640" + integrity sha512-bi0wuNJ1gw4ByNCV56H0Z4Q7D+SxUbwyG12Wxzbvqc89PXLRNR20LBcSUZRKpN0+YCPo6m0XZL/JLio3B52LTw== + dependencies: + "@ethereumjs/block" "^3.6.2" + "@ethereumjs/common" "^2.6.4" + "@ethereumjs/ethash" "^1.1.0" + debug "^4.3.3" + ethereumjs-util "^7.1.5" + level-mem "^5.0.1" + lru-cache "^5.1.1" + semaphore-async-await "^1.5.1" + +"@ethereumjs/common@^2.6.4", "@ethereumjs/common@^2.6.5": + version "2.6.5" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.5.tgz#0a75a22a046272579d91919cb12d84f2756e8d30" + integrity sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.1.5" + +"@ethereumjs/ethash@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/ethash/-/ethash-1.1.0.tgz#7c5918ffcaa9cb9c1dc7d12f77ef038c11fb83fb" + integrity sha512-/U7UOKW6BzpA+Vt+kISAoeDie1vAvY4Zy2KF5JJb+So7+1yKmJeJEHOGSnQIj330e9Zyl3L5Nae6VZyh2TJnAA== + dependencies: + "@ethereumjs/block" "^3.5.0" + "@types/levelup" "^4.3.0" + buffer-xor "^2.0.1" + ethereumjs-util "^7.1.1" + miller-rabin "^4.0.0" + +"@ethereumjs/tx@^3.5.2": + version "3.5.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.5.2.tgz#197b9b6299582ad84f9527ca961466fce2296c1c" + integrity sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw== + dependencies: + "@ethereumjs/common" "^2.6.4" + ethereumjs-util "^7.1.5" + +"@ethereumjs/vm@^5.8.0": + version "5.9.3" + resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-5.9.3.tgz#6d69202e4c132a4a1e1628ac246e92062e230823" + integrity sha512-Ha04TeF8goEglr8eL7hkkYyjhzdZS0PsoRURzYlTF6I0VVId5KjKb0N7MrA8GMgheN+UeTncfTgYx52D/WhEmg== + dependencies: + "@ethereumjs/block" "^3.6.3" + "@ethereumjs/blockchain" "^5.5.3" + "@ethereumjs/common" "^2.6.5" + "@ethereumjs/tx" "^3.5.2" + async-eventemitter "^0.2.4" + core-js-pure "^3.0.1" + debug "^4.3.3" + ethereumjs-util "^7.1.5" + functional-red-black-tree "^1.0.1" + mcl-wasm "^0.7.1" + merkle-patricia-tree "^4.2.4" + rustbn.js "~0.2.0" + +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.6.0", "@ethersproject/abi@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" + integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + +"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/address@5.7.0", "@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + +"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + +"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" + integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + +"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + +"@ethersproject/contracts@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" + integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + +"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf" + integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/wordlists" "^5.7.0" + +"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360" + integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hdnode" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + +"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102" + integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + +"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/providers@5.7.2": + version "5.7.2" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" + integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/basex" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + bech32 "1.1.4" + ws "7.4.6" + +"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" + integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" + integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + hash.js "1.1.7" + +"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/solidity@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" + integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/sha2" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + +"@ethersproject/units@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" + integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/wallet@5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d" + integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/hdnode" "^5.7.0" + "@ethersproject/json-wallets" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/random" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/wordlists" "^5.7.0" + +"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5" + integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@types/abstract-leveldown@*": + version "7.2.5" + resolved "https://registry.yarnpkg.com/@types/abstract-leveldown/-/abstract-leveldown-7.2.5.tgz#db2cf364c159fb1f12be6cd3549f56387eaf8d73" + integrity sha512-/2B0nQF4UdupuxeKTJA2+Rj1D+uDemo6P4kMwKCpbfpnzeVaWSELTsAw4Lxn3VJD6APtRrZOCuYo+4nHUQfTfg== + +"@types/bn.js@^5.1.0": + version "5.1.5" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" + integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== + dependencies: + "@types/node" "*" + +"@types/level-errors@*": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/level-errors/-/level-errors-3.0.2.tgz#f33ec813c50780b547463da9ad8acac89ee457d9" + integrity sha512-gyZHbcQ2X5hNXf/9KS2qGEmgDe9EN2WDM3rJ5Ele467C0nA1sLhtmv1bZiPMDYfAYCfPWft0uQIaTvXbASSTRA== + +"@types/levelup@^4.3.0": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@types/levelup/-/levelup-4.3.3.tgz#4dc2b77db079b1cf855562ad52321aa4241b8ef4" + integrity sha512-K+OTIjJcZHVlZQN1HmU64VtrC0jC3dXWQozuEIR9zVvltIk90zaGPM2AgT+fIkChpzHhFE3YnvFLCbLtzAmexA== + dependencies: + "@types/abstract-leveldown" "*" + "@types/level-errors" "*" + "@types/node" "*" + +"@types/node@*": + version "20.14.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.2.tgz#a5f4d2bcb4b6a87bffcaa717718c5a0f208f4a18" + integrity sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q== + dependencies: + undici-types "~5.26.4" + +"@types/pbkdf2@^3.0.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.2.tgz#2dc43808e9985a2c69ff02e2d2027bd4fe33e8dc" + integrity sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew== + dependencies: + "@types/node" "*" + +"@types/secp256k1@^4.0.1": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.6.tgz#d60ba2349a51c2cbc5e816dcd831a42029d376bf" + integrity sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ== + dependencies: + "@types/node" "*" + +abstract-leveldown@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a" + integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +abstract-leveldown@~6.2.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb" + integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw== + +async-eventemitter@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" + +async@^2.4.0: + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== + dependencies: + lodash "^4.17.14" + +base-x@^3.0.2: + version "3.0.9" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" + integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bech32@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + +blakejs@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" + integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== + +bn.js@^4.0.0, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + +browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer-xor@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-2.0.2.tgz#34f7c64f04c777a1f8aac5e661273bb9dd320289" + integrity sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ== + dependencies: + safe-buffer "^5.1.1" + +buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +command-exists@^1.2.8: + version "1.2.9" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + +commander@^8.1.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +core-js-pure@^3.0.1: + version "3.37.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.37.1.tgz#2b4b34281f54db06c9a9a5bd60105046900553bd" + integrity sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA== + +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +debug@^4.3.3: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + +deferred-leveldown@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" + integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== + dependencies: + abstract-leveldown "~6.2.1" + inherits "^2.0.3" + +elliptic@6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +elliptic@^6.5.4: + version "6.5.5" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" + integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +encoding-down@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" + integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== + dependencies: + abstract-leveldown "^6.2.1" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + +errno@~0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.4, ethereumjs-util@^7.1.5: + version "7.1.5" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" + integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + +ethers@^5.6.8: + version "5.7.2" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" + integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== + dependencies: + "@ethersproject/abi" "5.7.0" + "@ethersproject/abstract-provider" "5.7.0" + "@ethersproject/abstract-signer" "5.7.0" + "@ethersproject/address" "5.7.0" + "@ethersproject/base64" "5.7.0" + "@ethersproject/basex" "5.7.0" + "@ethersproject/bignumber" "5.7.0" + "@ethersproject/bytes" "5.7.0" + "@ethersproject/constants" "5.7.0" + "@ethersproject/contracts" "5.7.0" + "@ethersproject/hash" "5.7.0" + "@ethersproject/hdnode" "5.7.0" + "@ethersproject/json-wallets" "5.7.0" + "@ethersproject/keccak256" "5.7.0" + "@ethersproject/logger" "5.7.0" + "@ethersproject/networks" "5.7.1" + "@ethersproject/pbkdf2" "5.7.0" + "@ethersproject/properties" "5.7.0" + "@ethersproject/providers" "5.7.2" + "@ethersproject/random" "5.7.0" + "@ethersproject/rlp" "5.7.0" + "@ethersproject/sha2" "5.7.0" + "@ethersproject/signing-key" "5.7.0" + "@ethersproject/solidity" "5.7.0" + "@ethersproject/strings" "5.7.0" + "@ethersproject/transactions" "5.7.0" + "@ethersproject/units" "5.7.0" + "@ethersproject/wallet" "5.7.0" + "@ethersproject/web" "5.7.1" + "@ethersproject/wordlists" "5.7.0" + +evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +follow-redirects@^1.12.1: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + +functional-red-black-tree@^1.0.1, functional-red-black-tree@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +immediate@~3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" + integrity sha512-RrGCXRm/fRVqMIhqXrGEX9rRADavPiDFSoMb/k64i9XMk8uH4r/Omi5Ctierj6XzNecwDbO4WuFbDD1zmpl3Tg== + +inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +keccak@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" + integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +level-codec@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" + integrity sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ== + dependencies: + buffer "^5.6.0" + +level-concat-iterator@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" + integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== + +level-errors@^2.0.0, level-errors@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-2.0.1.tgz#2132a677bf4e679ce029f517c2f17432800c05c8" + integrity sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw== + dependencies: + errno "~0.1.1" + +level-iterator-stream@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c" + integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== + dependencies: + inherits "^2.0.4" + readable-stream "^3.4.0" + xtend "^4.0.2" + +level-mem@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-5.0.1.tgz#c345126b74f5b8aa376dc77d36813a177ef8251d" + integrity sha512-qd+qUJHXsGSFoHTziptAKXoLX87QjR7v2KMbqncDXPxQuCdsQlzmyX+gwrEHhlzn08vkf8TyipYyMmiC6Gobzg== + dependencies: + level-packager "^5.0.3" + memdown "^5.0.0" + +level-packager@^5.0.3: + version "5.1.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" + integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== + dependencies: + encoding-down "^6.3.0" + levelup "^4.3.2" + +level-supports@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" + integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== + dependencies: + xtend "^4.0.2" + +level-ws@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-2.0.0.tgz#207a07bcd0164a0ec5d62c304b4615c54436d339" + integrity sha512-1iv7VXx0G9ec1isqQZ7y5LmoZo/ewAsyDHNA8EFDW5hqH2Kqovm33nSFkSdnLLAK+I5FlT+lo5Cw9itGe+CpQA== + dependencies: + inherits "^2.0.3" + readable-stream "^3.1.0" + xtend "^4.0.1" + +levelup@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6" + integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== + dependencies: + deferred-leveldown "~5.3.0" + level-errors "~2.0.0" + level-iterator-stream "~4.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +lodash@^4.17.14: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +ltgt@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA== + +mcl-wasm@^0.7.1: + version "0.7.9" + resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f" + integrity sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +memdown@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-5.1.0.tgz#608e91a9f10f37f5b5fe767667a8674129a833cb" + integrity sha512-B3J+UizMRAlEArDjWHTMmadet+UKwHd3UjMgGBkZcKAxAYVPS9o0Yeiha4qvz7iGiL2Sb3igUft6p7nbFWctpw== + dependencies: + abstract-leveldown "~6.2.1" + functional-red-black-tree "~1.0.1" + immediate "~3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.2.0" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== + +merkle-patricia-tree@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-4.2.4.tgz#ff988d045e2bf3dfa2239f7fabe2d59618d57413" + integrity sha512-eHbf/BG6eGNsqqfbLED9rIqbsF4+sykEaBn6OLNs71tjclbMcMOk1tEPmJKcNcNCLkvbpY/lwyOlizWsqPNo8w== + dependencies: + "@types/levelup" "^4.3.0" + ethereumjs-util "^7.1.4" + level-mem "^5.0.1" + level-ws "^2.0.0" + readable-stream "^3.6.0" + semaphore-async-await "^1.5.1" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-gyp-build@^4.2.0: + version "4.8.1" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.1.tgz#976d3ad905e71b76086f4f0b0d3637fe79b6cda5" + integrity sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw== + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +pbkdf2@^3.0.17: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +prettier@^2.6.2: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +readable-stream@^3.1.0, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.2.4: + version "2.2.7" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" + integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== + dependencies: + bn.js "^5.2.0" + +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +scrypt-js@3.0.1, scrypt-js@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +secp256k1@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" + integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== + dependencies: + elliptic "^6.5.4" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +semaphore-async-await@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/semaphore-async-await/-/semaphore-async-await-1.5.1.tgz#857bef5e3644601ca4b9570b87e9df5ca12974fa" + integrity sha512-b/ptP11hETwYWpeilHXXQiV5UJNJl7ZWWooKRE5eBIYWoom6dZ0SluCIdCtKycsMtZgKWE01/qAw6jblw1YVhg== + +semver@^5.5.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +solc@0.8.19: + version "0.8.19" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.19.tgz#cac6541106ae3cff101c740042c7742aa56a2ed3" + integrity sha512-yqurS3wzC4LdEvmMobODXqprV4MYJcVtinuxgrp61ac8K2zz40vXA0eSAskSHPgv8dQo7Nux39i3QBsHx4pqyA== + dependencies: + command-exists "^1.2.8" + commander "^8.1.0" + follow-redirects "^1.12.1" + js-sha3 "0.8.0" + memorystream "^0.3.1" + semver "^5.5.0" + tmp "0.0.33" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +tmp@0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +ws@7.4.6: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + +xmldom@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.6.0.tgz#43a96ecb8beece991cef382c08397d82d4d0c46f" + integrity sha512-iAcin401y58LckRZ0TkI4k0VSM1Qg0KGSc3i8rU+xrxe19A/BN1zHyVSJY7uoutVlaTSzYyk/v5AmkewAP7jtg== + +xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== From 6c2e76851a75ec2325dc70b548b65a7abc3b62d4 Mon Sep 17 00:00:00 2001 From: Oighty Date: Mon, 17 Jun 2024 23:40:14 -0500 Subject: [PATCH 02/14] wip: more svg variants and updates --- .../derivatives/DerivativeCardDark.sol | 435 ++++++++++++++++++ .../derivatives/DerivativeCardGradient.sol | 371 +++++++++++++++ ...vativeCard.sol => DerivativeCardLight.sol} | 43 +- utils/hot-chain-svg/src/index.js | 2 +- 4 files changed, 828 insertions(+), 23 deletions(-) create mode 100644 src/modules/derivatives/DerivativeCardDark.sol create mode 100644 src/modules/derivatives/DerivativeCardGradient.sol rename src/modules/derivatives/{DerivativeCard.sol => DerivativeCardLight.sol} (92%) diff --git a/src/modules/derivatives/DerivativeCardDark.sol b/src/modules/derivatives/DerivativeCardDark.sol new file mode 100644 index 00000000..60a20a6e --- /dev/null +++ b/src/modules/derivatives/DerivativeCardDark.sol @@ -0,0 +1,435 @@ +/// SPDX-License-Identifier: BSL-1.1 +pragma solidity 0.8.19; + +import {svg, utils} from "src/lib/SVG.sol"; + +contract DerivativeCardDark { + + struct Info { + string derivativeType; + string baseAssetSymbol; + string quoteAssetSymbol; + string tokenId; + string tokenAddress; + string erc20Address; + Property[] properties; + } + + struct Property { + string key; + string stringValue; + uint256 value; + } + + struct Colors { + string blue; + string lightBlue; + string green; + string yellowGreen; + string yellow; + string orange; + string red; + } + + string internal constant STROKE = 'stroke="#000" stroke-miterlimit="10" stroke-width="4px" '; + string internal constant TEXT_STYLE= 'font-family="\'Menlo\', monospace" fill="white"'; + + Colors internal colors; + + constructor() { + colors = Colors({ + blue: 'rgb(110, 148, 240)', + lightBlue: 'rgb(118, 189, 242)', + green: 'rgb(206, 244, 117)', + yellowGreen: 'rgb(243, 244, 189)', + yellow: 'rgb(243, 244, 189)', + orange: 'rgb(246, 172, 84)', + red: 'rgb(242, 103, 64)' + }); + } + + function render(Info memory tokenInfo) internal view returns (string memory) { + return string.concat( + '', + svg.el('defs', utils.NULL, string.concat(fullGradient(), fullRadialGradient(), blueGreenGradient(), orangeRedGradient())), + svg.rect( + string.concat( + svg.prop('x', '5'), + svg.prop('y', '5'), + svg.prop('width', '280'), + svg.prop('height', '490'), + svg.prop('fill', 'rgb(30, 30, 30)'), + svg.prop('rx', '10'), + svg.prop('ry', '10') + ), + utils.NULL + ), + svg.text( + string.concat( + svg.prop('x', '145'), + svg.prop('y', '40'), + svg.prop('font-size', '20'), + svg.prop('text-anchor', 'middle'), + TEXT_STYLE + ), + tokenInfo.derivativeType + ), + svg.text( + string.concat( + svg.prop('x', '145'), + svg.prop('y', '100'), + svg.prop('font-size', '56'), + svg.prop('text-anchor', 'middle'), + // svg.prop('font-family', 'Menlo, monospace'), + // svg.prop('fill', "url('#orangeRedGradient')") + TEXT_STYLE + ), + tokenInfo.baseAssetSymbol + ), + progressBar(tokenInfo.properties[0].value, tokenInfo.properties[1].value), + svg.text( + string.concat( + svg.prop('x', '60'), + svg.prop('y', '200'), + svg.prop('font-size', '12'), + svg.prop('text-anchor', 'middle'), + TEXT_STYLE + ), + tokenInfo.properties[0].stringValue + ), + svg.text( + string.concat( + svg.prop('x', '230'), + svg.prop('y', '200'), + svg.prop('font-size', '12'), + svg.prop('text-anchor', 'middle'), + TEXT_STYLE + ), + tokenInfo.properties[1].stringValue + ), + svg.g( + string.concat( + svg.prop('transform', 'translate(72, 240) scale(0.15)') + ), + logo() + ), + // svg.text( + // string.concat( + // svg.prop('x', '20'), + // svg.prop('y', '360'), + // svg.prop('font-size', '16'), + // svg.prop('text-anchor', 'left'), + // TEXT_STYLE + // ), + // string.concat( + // tokenInfo.properties[0].key, + // ': ', + // tokenInfo.properties[0].stringValue + // ) + // ), + // svg.text( + // string.concat( + // svg.prop('x', '20'), + // svg.prop('y', '390'), + // svg.prop('font-size', '16'), + // svg.prop('text-anchor', 'left'), + // TEXT_STYLE + // ), + // string.concat( + // tokenInfo.properties[1].key, + // ': ', + // tokenInfo.properties[1].stringValue + // ) + // ), + // svg.g( + // string.concat( + // svg.prop('transform', 'translate(80, 430) scale(0.05)') + // ), + // wordmark() + // ), + svg.text( + string.concat( + svg.prop('x', '145'), + svg.prop('y', '460'), + svg.prop('font-size', '10'), + svg.prop('text-anchor', 'middle'), + TEXT_STYLE + ), + tokenInfo.tokenAddress + ), + svg.text( + string.concat( + svg.prop('x', '145'), + svg.prop('y', '480'), + svg.prop('font-size', '10'), + svg.prop('text-anchor', 'middle'), + TEXT_STYLE + ), + string.concat('ID: ', tokenInfo.tokenId) + ), + '' + ); + } + + function logo() internal pure returns (string memory) { + return svg.path( + string.concat( + svg.prop('fill' , "url('#fullRadialGradient')"), + STROKE, + svg.prop('d', 'M0.34668 818.666L20.3467 852.666L474.347 590.666V838.666H513.347V590.666L966.347 852.666L986.347 818.666L532.347 556.666L746.347 433.666L726.347 399.666L513.347 522.666L514.347 0.665527H474.347V522.666L260.347 399.666L240.347 433.666L454.347 556.666L0.34668 818.666Z') + ), + utils.NULL + ); + } + + function wordmark() internal pure returns (string memory) { + return svg.path( + string.concat( + svg.prop('fill' , "url('#fullGradient')"), + STROKE, + svg.prop('d', 'M2078.65 610.666H2118.65C2118.65 720.666 2205.65 799.666 2327.65 799.666C2439.65 799.666 2528.65 738.666 2528.65 629.666C2528.65 526.666 2460.65 465.666 2318.65 428.666C2167.65 397.666 2090.65 318.666 2090.65 201.666C2090.65 84.6656 2204.65 -2.33439 2323.65 0.665612C2450.65 -2.33439 2559.65 90.6656 2559.65 223.666H2521.65C2521.65 112.666 2432.65 40.6656 2323.65 40.6656C2223.65 40.6656 2130.65 110.666 2130.65 201.666C2130.65 300.666 2200.65 360.666 2331.65 394.666C2492.65 431.666 2569.65 505.666 2569.65 624.666C2569.65 743.666 2461.65 839.666 2323.65 839.666C2185.65 839.666 2078.65 742.666 2078.65 610.666ZM1926.65 820.666V20.6656H1966.65V820.666H1926.65ZM1005.65 801.666L1035.65 831.666L1416.65 450.666L1797.65 830.666L1826.65 800.666L1445.65 419.666L1826.65 40.6656L1796.65 10.6656L1415.65 391.666L1034.65 11.6656L1005.65 41.6656L1386.65 419.666L1005.65 801.666ZM45.6533 419.666C45.6533 627.666 212.653 794.666 419.653 794.666C611.653 794.666 793.653 648.666 793.653 419.666C793.653 190.666 611.653 45.6656 419.653 45.6656C212.653 45.6656 45.6533 213.666 45.6533 419.666ZM0.65332 419.666C0.65332 188.666 188.653 0.665612 419.653 0.665612C582.653 0.665612 761.653 103.666 817.653 299.666L869.653 20.6656H910.653L833.653 419.666L910.653 820.666H869.653L817.653 540.666C761.653 736.666 582.653 839.666 419.653 839.666C188.653 839.666 0.65332 651.666 0.65332 419.666Z') + ), + utils.NULL + ); + } + + function fullGradient() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop('id', 'fullGradient') + ), + string.concat( + svg.gradientStop( + 2, + colors.blue, + utils.NULL + ), + svg.gradientStop( + 10, + colors.lightBlue, + utils.NULL + ), + svg.gradientStop( + 32, + colors.green, + utils.NULL + ), + svg.gradientStop( + 49, + colors.yellowGreen, + utils.NULL + ), + svg.gradientStop( + 52, + colors.yellow, + utils.NULL + ), + svg.gradientStop( + 79, + colors.orange, + utils.NULL + ), + svg.gradientStop( + 100, + colors.red, + utils.NULL + ) + ) + ); + } + + function fullRadialGradient() internal view returns (string memory) { + return svg.radialGradient( + string.concat( + svg.prop('id', 'fullRadialGradient'), + svg.prop('gradientTransform', 'translate(0,0.15)') + ), + string.concat( + svg.gradientStop( + 2, + colors.blue, + utils.NULL + ), + svg.gradientStop( + 10, + colors.lightBlue, + utils.NULL + ), + svg.gradientStop( + 32, + colors.green, + utils.NULL + ), + svg.gradientStop( + 49, + colors.yellowGreen, + utils.NULL + ), + svg.gradientStop( + 52, + colors.yellow, + utils.NULL + ), + svg.gradientStop( + 79, + colors.orange, + utils.NULL + ), + svg.gradientStop( + 100, + colors.red, + utils.NULL + ) + ) + ); + } + + function blueGreenGradient() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop('id', 'blueGreenGradient'), + svg.prop('gradientUnits', 'userSpaceOnUse') + ), + string.concat( + svg.gradientStop( + 20, + colors.blue, + utils.NULL + ), + svg.gradientStop( + 40, + colors.lightBlue, + utils.NULL + ), + svg.gradientStop( + 60, + colors.green, + utils.NULL + ) + ) + ); + } + + function orangeRedGradient() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop('id', 'orangeRedGradient'), + svg.prop('gradientTransform', 'rotate(90)') + ), + string.concat( + svg.gradientStop( + 30, + colors.orange, + utils.NULL + ), + svg.gradientStop( + 90, + colors.red, + utils.NULL + ) + ) + ); + } + + function progressBar(uint256 start, uint256 end) internal view returns (string memory) { + uint256 currentTime = 1717200000 + 60 * 86400; // block.timestamp; + + bool started = start <= currentTime; + + uint256 progress = started ? (currentTime - start) * 100 / (end - start) : 0; + // progress can be at most 100 + progress = progress > 100 ? 100 : progress; + + uint256 len = (168 * progress) / 100; + + string memory startBar = svg.line( + string.concat( + svg.prop('x1', '60'), + svg.prop('y1', '155'), + svg.prop('x2', '60'), + svg.prop('y2', '175'), + svg.prop('stroke', started ? colors.blue : 'grey'), + svg.prop('stroke-width', '6') + ), + utils.NULL + ); + + string memory endBar = svg.line( + string.concat( + svg.prop('x1', '230'), + svg.prop('y1', '155'), + svg.prop('x2', '230'), + svg.prop('y2', '175'), + svg.prop('stroke', progress == 100 ? colors.green : 'grey'), + svg.prop('stroke-width', '6') + ), + utils.NULL + ); + + + string memory progressLine = svg.line( + string.concat( + svg.prop('x1', '62'), + svg.prop('y1', '165'), + svg.prop('x2', utils.uint2str(62 + len)), + svg.prop('y2', '165'), + svg.prop('stroke', "url('#blueGreenGradient')"), + svg.prop('stroke-width', '6') + ), + utils.NULL + ); + + string memory shadowLine = svg.line( + string.concat( + svg.prop('x1', '63'), + svg.prop('y1', '165'), + svg.prop('x2', '230'), + svg.prop('y2', '165'), + svg.prop('stroke', 'grey'), + svg.prop('stroke-width', '4') + ), + utils.NULL + ); + + string memory progressCircle = svg.circle( + string.concat( + svg.prop('cx', utils.uint2str(62 + len)), + svg.prop('cy', '165'), + svg.prop('r', '6'), + svg.prop('fill', "url('#blueGreenGradient')") + ), + utils.NULL + ); + + return svg.g( + utils.NULL, + string.concat( + startBar, + shadowLine, + progressLine, + endBar, + progress < 5 || progress > 95 ? '' : progressCircle + ) + ); + } + + function example() external view returns (string memory) { + Property[] memory properties = new Property[](2); + properties[0] = Property({key: "Vesting Start", stringValue: "2024-06-01", value: 1717200000}); + properties[1] = Property({key: "Vesting End", stringValue: "2024-09-01", value: 1725148800}); + + + Info memory info = Info({ + derivativeType: "Linear Vesting", + baseAssetSymbol: "XYZ", + quoteAssetSymbol: "", + tokenId: "123456", + tokenAddress: "0x1234567890123456789012345678901234567890", + erc20Address: "0xfedcba0987654321fedcba0987654321fedcba09", + properties: properties + }); + + return render(info); + } + +} \ No newline at end of file diff --git a/src/modules/derivatives/DerivativeCardGradient.sol b/src/modules/derivatives/DerivativeCardGradient.sol new file mode 100644 index 00000000..92bbea49 --- /dev/null +++ b/src/modules/derivatives/DerivativeCardGradient.sol @@ -0,0 +1,371 @@ +/// SPDX-License-Identifier: BSL-1.1 +pragma solidity 0.8.19; + +import {svg, utils} from "src/lib/SVG.sol"; + +contract DerivativeCardDark { + + struct Info { + string derivativeType; + string baseAssetSymbol; + string quoteAssetSymbol; + Property[] properties; + } + + struct Property { + string key; + string value; + } + + struct Colors { + string blue; + string lightBlue; + string green; + string yellowGreen; + string yellow; + string orange; + string red; + } + + string internal constant STROKE = 'stroke="#000" stroke-miterlimit="10" stroke-width="4px" '; + string internal constant TEXT_STYLE= 'font-family="\'Menlo\', monospace" fill="black"'; + + Colors internal colors; + + constructor() { + colors = Colors({ + blue: 'rgb(110, 148, 240)', + lightBlue: 'rgb(118, 189, 242)', + green: 'rgb(206, 244, 117)', + yellowGreen: 'rgb(243, 244, 189)', + yellow: 'rgb(243, 244, 189)', + orange: 'rgb(246, 172, 84)', + red: 'rgb(242, 103, 64)' + }); + } + + function render(Info memory tokenInfo) internal view returns (string memory) { + return string.concat( + '', + svg.el('defs', utils.NULL, string.concat(fullGradient(), fullVertGradient(), blueGreenGradient(), orangeRedGradient())), + svg.rect( + string.concat( + svg.prop('x', '5'), + svg.prop('y', '5'), + svg.prop('width', '280'), + svg.prop('height', '490'), + svg.prop('fill', "url('#orangeRedGradient')"), + svg.prop('stroke', 'black'), + svg.prop('rx', '10'), + svg.prop('ry', '10') + ), + utils.NULL + ), + svg.text( + string.concat( + svg.prop('x', '145'), + svg.prop('y', '40'), + svg.prop('font-size', '20'), + svg.prop('text-anchor', 'middle'), + TEXT_STYLE + ), + tokenInfo.derivativeType + ), + svg.text( + string.concat( + svg.prop('x', '145'), + svg.prop('y', '100'), + svg.prop('font-size', '56'), + svg.prop('text-anchor', 'middle'), + TEXT_STYLE + ), + tokenInfo.baseAssetSymbol + ), + progressBar(80), + svg.g( + string.concat( + svg.prop('transform', 'translate(96, 200) scale(0.1)') + ), + logo() + ), + svg.text( + string.concat( + svg.prop('x', '20'), + svg.prop('y', '350'), + svg.prop('font-size', '16'), + svg.prop('text-anchor', 'left'), + TEXT_STYLE + ), + string.concat( + tokenInfo.properties[0].key, + ': ', + tokenInfo.properties[0].value + ) + ), + svg.text( + string.concat( + svg.prop('x', '20'), + svg.prop('y', '380'), + svg.prop('font-size', '16'), + svg.prop('text-anchor', 'left'), + TEXT_STYLE + ), + string.concat( + tokenInfo.properties[1].key, + ': ', + tokenInfo.properties[1].value + ) + ), + // svg.g( + // string.concat( + // svg.prop('transform', 'translate(80, 430) scale(0.05)') + // ), + // wordmark() + // ), + '' + ); + } + + function logo() internal pure returns (string memory) { + return svg.path( + string.concat( + svg.prop('fill' , 'white'), + STROKE, + svg.prop('d', 'M0.34668 818.666L20.3467 852.666L474.347 590.666V838.666H513.347V590.666L966.347 852.666L986.347 818.666L532.347 556.666L746.347 433.666L726.347 399.666L513.347 522.666L514.347 0.665527H474.347V522.666L260.347 399.666L240.347 433.666L454.347 556.666L0.34668 818.666Z') + ), + utils.NULL + ); + } + + function wordmark() internal pure returns (string memory) { + return svg.path( + string.concat( + svg.prop('fill' , "url('#fullGradient')"), + STROKE, + svg.prop('d', 'M2078.65 610.666H2118.65C2118.65 720.666 2205.65 799.666 2327.65 799.666C2439.65 799.666 2528.65 738.666 2528.65 629.666C2528.65 526.666 2460.65 465.666 2318.65 428.666C2167.65 397.666 2090.65 318.666 2090.65 201.666C2090.65 84.6656 2204.65 -2.33439 2323.65 0.665612C2450.65 -2.33439 2559.65 90.6656 2559.65 223.666H2521.65C2521.65 112.666 2432.65 40.6656 2323.65 40.6656C2223.65 40.6656 2130.65 110.666 2130.65 201.666C2130.65 300.666 2200.65 360.666 2331.65 394.666C2492.65 431.666 2569.65 505.666 2569.65 624.666C2569.65 743.666 2461.65 839.666 2323.65 839.666C2185.65 839.666 2078.65 742.666 2078.65 610.666ZM1926.65 820.666V20.6656H1966.65V820.666H1926.65ZM1005.65 801.666L1035.65 831.666L1416.65 450.666L1797.65 830.666L1826.65 800.666L1445.65 419.666L1826.65 40.6656L1796.65 10.6656L1415.65 391.666L1034.65 11.6656L1005.65 41.6656L1386.65 419.666L1005.65 801.666ZM45.6533 419.666C45.6533 627.666 212.653 794.666 419.653 794.666C611.653 794.666 793.653 648.666 793.653 419.666C793.653 190.666 611.653 45.6656 419.653 45.6656C212.653 45.6656 45.6533 213.666 45.6533 419.666ZM0.65332 419.666C0.65332 188.666 188.653 0.665612 419.653 0.665612C582.653 0.665612 761.653 103.666 817.653 299.666L869.653 20.6656H910.653L833.653 419.666L910.653 820.666H869.653L817.653 540.666C761.653 736.666 582.653 839.666 419.653 839.666C188.653 839.666 0.65332 651.666 0.65332 419.666Z') + ), + utils.NULL + ); + } + + function fullGradient() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop('id', 'fullGradient') + ), + string.concat( + svg.gradientStop( + 2, + colors.blue, + utils.NULL + ), + svg.gradientStop( + 10, + colors.lightBlue, + utils.NULL + ), + svg.gradientStop( + 32, + colors.green, + utils.NULL + ), + svg.gradientStop( + 49, + colors.yellowGreen, + utils.NULL + ), + svg.gradientStop( + 52, + colors.yellow, + utils.NULL + ), + svg.gradientStop( + 79, + colors.orange, + utils.NULL + ), + svg.gradientStop( + 100, + colors.red, + utils.NULL + ) + ) + ); + } + + function fullVertGradient() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop('id', 'fullVertGradient'), + svg.prop('gradientTransform', 'rotate(90)') + ), + string.concat( + svg.gradientStop( + 8, + colors.blue, + utils.NULL + ), + svg.gradientStop( + 20, + colors.lightBlue, + utils.NULL + ), + svg.gradientStop( + 42, + colors.green, + utils.NULL + ), + svg.gradientStop( + 59, + colors.yellowGreen, + utils.NULL + ), + svg.gradientStop( + 65, + colors.yellow, + utils.NULL + ), + svg.gradientStop( + 79, + colors.orange, + utils.NULL + ), + svg.gradientStop( + 100, + colors.red, + utils.NULL + ) + ) + ); + } + + function blueGreenGradient() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop('id', 'blueGreenGradient'), + svg.prop('gradientUnits', 'userSpaceOnUse') + ), + string.concat( + svg.gradientStop( + 20, + colors.blue, + utils.NULL + ), + svg.gradientStop( + 40, + colors.lightBlue, + utils.NULL + ), + svg.gradientStop( + 60, + colors.green, + utils.NULL + ) + ) + ); + } + + function orangeRedGradient() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop('id', 'orangeRedGradient'), + svg.prop('gradientTransform', 'rotate(60)') + ), + string.concat( + svg.gradientStop( + 30, + colors.orange, + utils.NULL + ), + svg.gradientStop( + 90, + colors.red, + utils.NULL + ) + ) + ); + } + + function progressBar(uint256 _progress) internal view returns (string memory) { + + uint256 len = 168 * _progress / 100; + + string memory startBar = svg.line( + string.concat( + svg.prop('x1', '60'), + svg.prop('y1', '135'), + svg.prop('x2', '60'), + svg.prop('y2', '155'), + svg.prop('stroke', colors.blue), + svg.prop('stroke-width', '6') + ), + utils.NULL + ); + + string memory endBarColor = _progress == 100 ? colors.green : 'grey'; + + string memory endBar = svg.line( + string.concat( + svg.prop('x1', '230'), + svg.prop('y1', '135'), + svg.prop('x2', '230'), + svg.prop('y2', '155'), + svg.prop('stroke', endBarColor), + svg.prop('stroke-width', '6') + ), + utils.NULL + ); + + + string memory progressLine = svg.line( + string.concat( + svg.prop('x1', '63'), + svg.prop('y1', '145'), + svg.prop('x2', utils.uint2str(62 + len)), + svg.prop('y2', '145'), + svg.prop('stroke', "url('#blueGreenGradient')"), + svg.prop('stroke-width', '6') + ), + utils.NULL + ); + + string memory shadowLine = svg.line( + string.concat( + svg.prop('x1', '63'), + svg.prop('y1', '145'), + svg.prop('x2', '230'), + svg.prop('y2', '145'), + svg.prop('stroke', 'grey'), + svg.prop('stroke-width', '4') + ), + utils.NULL + ); + + return svg.g( + utils.NULL, + string.concat( + startBar, + shadowLine, + progressLine, + endBar + ) + ); + } + + function example() external view returns (string memory) { + Property[] memory properties = new Property[](2); + properties[0] = Property({key: "Vesting Start", value: "2024-07-01"}); + properties[1] = Property({key: "Vesting End", value: "2024-09-01"}); + + + Info memory info = Info({ + derivativeType: "Linear Vesting", + baseAssetSymbol: "XYZ", + quoteAssetSymbol: "", + properties: properties + }); + + return render(info); + } + +} \ No newline at end of file diff --git a/src/modules/derivatives/DerivativeCard.sol b/src/modules/derivatives/DerivativeCardLight.sol similarity index 92% rename from src/modules/derivatives/DerivativeCard.sol rename to src/modules/derivatives/DerivativeCardLight.sol index 828a398e..b4c116d1 100644 --- a/src/modules/derivatives/DerivativeCard.sol +++ b/src/modules/derivatives/DerivativeCardLight.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.19; import {svg, utils} from "src/lib/SVG.sol"; -contract DerivativeCard { +contract DerivativeCardDark { struct Info { string derivativeType; @@ -28,6 +28,8 @@ contract DerivativeCard { } string internal constant STROKE = 'stroke="#000" stroke-miterlimit="10" stroke-width="4px" '; + string internal constant TEXT_STYLE= 'font-family="\'Menlo\', monospace" fill="#000"'; + Colors internal colors; constructor() { @@ -52,7 +54,8 @@ contract DerivativeCard { svg.prop('y', '5'), svg.prop('width', '280'), svg.prop('height', '490'), - svg.prop('fill', 'black'), + svg.prop('fill', 'rgb(200, 200, 200)'), + svg.prop('stroke', 'black'), svg.prop('rx', '10'), svg.prop('ry', '10') ), @@ -62,10 +65,9 @@ contract DerivativeCard { string.concat( svg.prop('x', '145'), svg.prop('y', '30'), - svg.prop('fill', 'white'), svg.prop('font-size', '20'), - svg.prop('font-family', "\'Courier New\', monospace"), - svg.prop('text-anchor', 'middle') + svg.prop('text-anchor', 'middle'), + TEXT_STYLE ), tokenInfo.derivativeType ), @@ -73,14 +75,13 @@ contract DerivativeCard { string.concat( svg.prop('x', '145'), svg.prop('y', '90'), - svg.prop('fill', 'white'), svg.prop('font-size', '56'), - svg.prop('font-family', "\'Courier New\', monospace"), - svg.prop('text-anchor', 'middle') + svg.prop('text-anchor', 'middle'), + TEXT_STYLE ), tokenInfo.baseAssetSymbol ), - progressBar(100), + progressBar(60), svg.g( string.concat( svg.prop('transform', 'translate(96, 200) scale(0.1)') @@ -91,10 +92,9 @@ contract DerivativeCard { string.concat( svg.prop('x', '20'), svg.prop('y', '350'), - svg.prop('fill', 'white'), svg.prop('font-size', '16'), - svg.prop('font-family', "\'Courier New\', monospace"), - svg.prop('text-anchor', 'left') + svg.prop('text-anchor', 'left'), + TEXT_STYLE ), string.concat( tokenInfo.properties[0].key, @@ -106,10 +106,9 @@ contract DerivativeCard { string.concat( svg.prop('x', '20'), svg.prop('y', '380'), - svg.prop('fill', 'white'), svg.prop('font-size', '16'), - svg.prop('font-family', "\'Courier New\', monospace"), - svg.prop('text-anchor', 'left') + svg.prop('text-anchor', 'left'), + TEXT_STYLE ), string.concat( tokenInfo.properties[1].key, @@ -117,12 +116,12 @@ contract DerivativeCard { tokenInfo.properties[1].value ) ), - svg.g( - string.concat( - svg.prop('transform', 'translate(80, 430) scale(0.05)') - ), - wordmark() - ), + // svg.g( + // string.concat( + // svg.prop('transform', 'translate(80, 430) scale(0.05)') + // ), + // wordmark() + // ), '' ); } @@ -131,7 +130,7 @@ contract DerivativeCard { return svg.path( string.concat( svg.prop('fill' , "url('#fullVertGradient')"), - STROKE, + // STROKE, svg.prop('d', 'M0.34668 818.666L20.3467 852.666L474.347 590.666V838.666H513.347V590.666L966.347 852.666L986.347 818.666L532.347 556.666L746.347 433.666L726.347 399.666L513.347 522.666L514.347 0.665527H474.347V522.666L260.347 399.666L240.347 433.666L454.347 556.666L0.34668 818.666Z') ), utils.NULL diff --git a/utils/hot-chain-svg/src/index.js b/utils/hot-chain-svg/src/index.js index 4f71faea..b069547c 100644 --- a/utils/hot-chain-svg/src/index.js +++ b/utils/hot-chain-svg/src/index.js @@ -6,7 +6,7 @@ const call = require('./call'); const compile = require('./compile'); const deploy = require('./deploy'); -const SOURCE = path.join(__dirname, '..', '..', '..', 'src', 'modules', 'derivatives', 'DerivativeCard.sol'); +const SOURCE = path.join(__dirname, '..', '..', '..', 'src', 'modules', 'derivatives', 'DerivativeCardDark.sol'); const PROJECT_DIR = path.join(__dirname, '..', '..', '..'); async function main() { From 9c4e56fc175fe11a50e0425688c6cb06e2fde432 Mon Sep 17 00:00:00 2001 From: Oighty Date: Tue, 18 Jun 2024 12:14:50 -0500 Subject: [PATCH 03/14] wip: more liv image updates (including animation) --- .../derivatives/DerivativeCardDark.sol | 306 ++++++++++-------- .../derivatives/DerivativeCardGradient.sol | 19 +- 2 files changed, 184 insertions(+), 141 deletions(-) diff --git a/src/modules/derivatives/DerivativeCardDark.sol b/src/modules/derivatives/DerivativeCardDark.sol index 60a20a6e..7941ce0f 100644 --- a/src/modules/derivatives/DerivativeCardDark.sol +++ b/src/modules/derivatives/DerivativeCardDark.sol @@ -51,16 +51,30 @@ contract DerivativeCardDark { function render(Info memory tokenInfo) internal view returns (string memory) { return string.concat( '', - svg.el('defs', utils.NULL, string.concat(fullGradient(), fullRadialGradient(), blueGreenGradient(), orangeRedGradient())), + svg.el('defs', utils.NULL, string.concat(fullGradient(), fullGradient90(), fullGradientReverse90(), blueGreenGradient(), orangeRedGradient())), svg.rect( string.concat( - svg.prop('x', '5'), - svg.prop('y', '5'), - svg.prop('width', '280'), - svg.prop('height', '490'), + svg.prop('x', '0'), + svg.prop('y', '0'), + svg.prop('width', '290'), + svg.prop('height', '500'), svg.prop('fill', 'rgb(30, 30, 30)'), - svg.prop('rx', '10'), - svg.prop('ry', '10') + svg.prop('rx', '25'), + svg.prop('ry', '25') + ), + utils.NULL + ), + svg.rect( + string.concat( + svg.prop('x', '8'), + svg.prop('y', '8'), + svg.prop('width', '274'), + svg.prop('height', '484'), + svg.prop('fill', 'none'), + svg.prop('stroke', "url('#fullGradientReverse90')"), + svg.prop('stroke-width', '2'), + svg.prop('rx', '20'), + svg.prop('ry', '20') ), utils.NULL ), @@ -107,46 +121,7 @@ contract DerivativeCardDark { ), tokenInfo.properties[1].stringValue ), - svg.g( - string.concat( - svg.prop('transform', 'translate(72, 240) scale(0.15)') - ), - logo() - ), - // svg.text( - // string.concat( - // svg.prop('x', '20'), - // svg.prop('y', '360'), - // svg.prop('font-size', '16'), - // svg.prop('text-anchor', 'left'), - // TEXT_STYLE - // ), - // string.concat( - // tokenInfo.properties[0].key, - // ': ', - // tokenInfo.properties[0].stringValue - // ) - // ), - // svg.text( - // string.concat( - // svg.prop('x', '20'), - // svg.prop('y', '390'), - // svg.prop('font-size', '16'), - // svg.prop('text-anchor', 'left'), - // TEXT_STYLE - // ), - // string.concat( - // tokenInfo.properties[1].key, - // ': ', - // tokenInfo.properties[1].stringValue - // ) - // ), - // svg.g( - // string.concat( - // svg.prop('transform', 'translate(80, 430) scale(0.05)') - // ), - // wordmark() - // ), + logo(), svg.text( string.concat( svg.prop('x', '145'), @@ -172,24 +147,119 @@ contract DerivativeCardDark { } function logo() internal pure returns (string memory) { - return svg.path( - string.concat( - svg.prop('fill' , "url('#fullRadialGradient')"), - STROKE, - svg.prop('d', 'M0.34668 818.666L20.3467 852.666L474.347 590.666V838.666H513.347V590.666L966.347 852.666L986.347 818.666L532.347 556.666L746.347 433.666L726.347 399.666L513.347 522.666L514.347 0.665527H474.347V522.666L260.347 399.666L240.347 433.666L454.347 556.666L0.34668 818.666Z') + return string.concat( + svg.rect( + string.concat( + svg.prop('x', '143'), + svg.prop('y', '240'), + svg.prop('width', '6'), + svg.prop('height', '125'), + svg.prop('fill', "url('#fullGradientReverse90')") + ), + utils.NULL ), - utils.NULL + svg.rect( + string.concat( + svg.prop('x', '79'), + svg.prop('y', '246'), + svg.prop('width', '6'), + svg.prop('height', '125'), + svg.prop('fill', "url('#fullGradient90')"), + svg.prop('transform', 'rotate(-60 145 250)') + ), + utils.NULL + ), + svg.rect( + string.concat( + svg.prop('x', '206'), + svg.prop('y', '244'), + svg.prop('width', '6'), + svg.prop('height', '125'), + svg.prop('fill', "url('#fullGradient90')"), + svg.prop('transform', 'rotate(60 145 250)') + ), + utils.NULL + ) ); } - function wordmark() internal pure returns (string memory) { - return svg.path( - string.concat( - svg.prop('fill' , "url('#fullGradient')"), - STROKE, - svg.prop('d', 'M2078.65 610.666H2118.65C2118.65 720.666 2205.65 799.666 2327.65 799.666C2439.65 799.666 2528.65 738.666 2528.65 629.666C2528.65 526.666 2460.65 465.666 2318.65 428.666C2167.65 397.666 2090.65 318.666 2090.65 201.666C2090.65 84.6656 2204.65 -2.33439 2323.65 0.665612C2450.65 -2.33439 2559.65 90.6656 2559.65 223.666H2521.65C2521.65 112.666 2432.65 40.6656 2323.65 40.6656C2223.65 40.6656 2130.65 110.666 2130.65 201.666C2130.65 300.666 2200.65 360.666 2331.65 394.666C2492.65 431.666 2569.65 505.666 2569.65 624.666C2569.65 743.666 2461.65 839.666 2323.65 839.666C2185.65 839.666 2078.65 742.666 2078.65 610.666ZM1926.65 820.666V20.6656H1966.65V820.666H1926.65ZM1005.65 801.666L1035.65 831.666L1416.65 450.666L1797.65 830.666L1826.65 800.666L1445.65 419.666L1826.65 40.6656L1796.65 10.6656L1415.65 391.666L1034.65 11.6656L1005.65 41.6656L1386.65 419.666L1005.65 801.666ZM45.6533 419.666C45.6533 627.666 212.653 794.666 419.653 794.666C611.653 794.666 793.653 648.666 793.653 419.666C793.653 190.666 611.653 45.6656 419.653 45.6656C212.653 45.6656 45.6533 213.666 45.6533 419.666ZM0.65332 419.666C0.65332 188.666 188.653 0.665612 419.653 0.665612C582.653 0.665612 761.653 103.666 817.653 299.666L869.653 20.6656H910.653L833.653 419.666L910.653 820.666H869.653L817.653 540.666C761.653 736.666 582.653 839.666 419.653 839.666C188.653 839.666 0.65332 651.666 0.65332 419.666Z') + function fullGradientStops() internal view returns (string memory) { + return string.concat( + svg.gradientStop( + 2, + colors.blue, + utils.NULL ), - utils.NULL + svg.gradientStop( + 10, + colors.lightBlue, + utils.NULL + ), + svg.gradientStop( + 32, + colors.green, + utils.NULL + ), + svg.gradientStop( + 49, + colors.yellowGreen, + utils.NULL + ), + svg.gradientStop( + 52, + colors.yellow, + utils.NULL + ), + svg.gradientStop( + 79, + colors.orange, + utils.NULL + ), + svg.gradientStop( + 100, + colors.red, + utils.NULL + ) + ); + } + + function fullGradientReverseStops() internal view returns (string memory) { + return string.concat( + svg.gradientStop( + 2, + colors.red, + utils.NULL + ), + svg.gradientStop( + 21, + colors.orange, + utils.NULL + ), + svg.gradientStop( + 48, + colors.yellow, + utils.NULL + ), + svg.gradientStop( + 51, + colors.yellowGreen, + utils.NULL + ), + svg.gradientStop( + 68, + colors.green, + utils.NULL + ), + svg.gradientStop( + 90, + colors.lightBlue, + utils.NULL + ), + svg.gradientStop( + 100, + colors.blue, + utils.NULL + ) ); } @@ -198,89 +268,27 @@ contract DerivativeCardDark { string.concat( svg.prop('id', 'fullGradient') ), - string.concat( - svg.gradientStop( - 2, - colors.blue, - utils.NULL - ), - svg.gradientStop( - 10, - colors.lightBlue, - utils.NULL - ), - svg.gradientStop( - 32, - colors.green, - utils.NULL - ), - svg.gradientStop( - 49, - colors.yellowGreen, - utils.NULL - ), - svg.gradientStop( - 52, - colors.yellow, - utils.NULL - ), - svg.gradientStop( - 79, - colors.orange, - utils.NULL - ), - svg.gradientStop( - 100, - colors.red, - utils.NULL - ) - ) + fullGradientStops() ); } - function fullRadialGradient() internal view returns (string memory) { - return svg.radialGradient( + function fullGradient90() internal view returns (string memory) { + return svg.linearGradient( string.concat( - svg.prop('id', 'fullRadialGradient'), - svg.prop('gradientTransform', 'translate(0,0.15)') + svg.prop('id', 'fullGradient90'), + svg.prop('gradientTransform', 'rotate(90)') ), + fullGradientStops() + ); + } + + function fullGradientReverse90() internal view returns (string memory) { + return svg.linearGradient( string.concat( - svg.gradientStop( - 2, - colors.blue, - utils.NULL - ), - svg.gradientStop( - 10, - colors.lightBlue, - utils.NULL - ), - svg.gradientStop( - 32, - colors.green, - utils.NULL - ), - svg.gradientStop( - 49, - colors.yellowGreen, - utils.NULL - ), - svg.gradientStop( - 52, - colors.yellow, - utils.NULL - ), - svg.gradientStop( - 79, - colors.orange, - utils.NULL - ), - svg.gradientStop( - 100, - colors.red, - utils.NULL - ) - ) + svg.prop('id', 'fullGradientReverse90'), + svg.prop('gradientTransform', 'rotate(90)') + ), + fullGradientReverseStops() ); } @@ -332,7 +340,7 @@ contract DerivativeCardDark { } function progressBar(uint256 start, uint256 end) internal view returns (string memory) { - uint256 currentTime = 1717200000 + 60 * 86400; // block.timestamp; + uint256 currentTime = 1717200000 + 70 * 86400; // block.timestamp; bool started = start <= currentTime; @@ -379,6 +387,27 @@ contract DerivativeCardDark { utils.NULL ); + string memory animateLine = svg.rect( + string.concat( + svg.prop('x', '62'), + svg.prop('y', '161'), + svg.prop('width', '16'), + svg.prop('height', '8'), + svg.prop('fill', "url('#blueGreenGradient')"), + svg.prop('rx', '4'), + svg.prop('ry', '4') + ), + svg.el('animate', + string.concat( + svg.prop('attributeName', 'x'), + svg.prop('values', string.concat('62;', utils.uint2str(62 + len - 16), ';')), + svg.prop('dur', '3s'), + svg.prop('repeatCount', 'indefinite') + ), + utils.NULL + ) + ); + string memory shadowLine = svg.line( string.concat( svg.prop('x1', '63'), @@ -407,6 +436,7 @@ contract DerivativeCardDark { startBar, shadowLine, progressLine, + progress < 5 ? '' : animateLine, endBar, progress < 5 || progress > 95 ? '' : progressCircle ) diff --git a/src/modules/derivatives/DerivativeCardGradient.sol b/src/modules/derivatives/DerivativeCardGradient.sol index 92bbea49..bdd5bcc9 100644 --- a/src/modules/derivatives/DerivativeCardGradient.sol +++ b/src/modules/derivatives/DerivativeCardGradient.sol @@ -55,16 +55,29 @@ contract DerivativeCardDark { svg.prop('width', '280'), svg.prop('height', '490'), svg.prop('fill', "url('#orangeRedGradient')"), + svg.prop('stroke', 'none'), + svg.prop('rx', '20'), + svg.prop('ry', '20') + ), + utils.NULL + ), + svg.rect( + string.concat( + svg.prop('x', '15'), + svg.prop('y', '15'), + svg.prop('width', '260'), + svg.prop('height', '470'), + svg.prop('fill', 'none'), svg.prop('stroke', 'black'), - svg.prop('rx', '10'), - svg.prop('ry', '10') + svg.prop('rx', '20'), + svg.prop('ry', '20') ), utils.NULL ), svg.text( string.concat( svg.prop('x', '145'), - svg.prop('y', '40'), + svg.prop('y', '45'), svg.prop('font-size', '20'), svg.prop('text-anchor', 'middle'), TEXT_STYLE From b8616283369d19befef61604e44de77600d170c9 Mon Sep 17 00:00:00 2001 From: Oighty Date: Wed, 19 Jun 2024 10:45:58 -0500 Subject: [PATCH 04/14] feat: compiles and integrated into LIV --- src/lib/ERC6909Metadata.sol | 6 + src/lib/SVG.sol | 312 ++++------ .../derivatives/DerivativeCardDark.sol | 465 --------------- .../derivatives/DerivativeCardGradient.sol | 384 ------------ .../derivatives/DerivativeCardLight.sol | 371 ------------ src/modules/derivatives/LinearVesting.sol | 42 +- src/modules/derivatives/LinearVestingCard.sol | 553 ++++++++++++++++++ .../mocks/MockDerivativeModule.sol | 2 + utils/hot-chain-svg/src/compile.js | 6 +- utils/hot-chain-svg/src/index.js | 2 +- 10 files changed, 724 insertions(+), 1419 deletions(-) delete mode 100644 src/modules/derivatives/DerivativeCardDark.sol delete mode 100644 src/modules/derivatives/DerivativeCardGradient.sol delete mode 100644 src/modules/derivatives/DerivativeCardLight.sol create mode 100644 src/modules/derivatives/LinearVestingCard.sol diff --git a/src/lib/ERC6909Metadata.sol b/src/lib/ERC6909Metadata.sol index eea62145..5229e49e 100644 --- a/src/lib/ERC6909Metadata.sol +++ b/src/lib/ERC6909Metadata.sol @@ -19,4 +19,10 @@ abstract contract ERC6909Metadata { /// @param tokenId_ The ID of the token /// @return uint8 The number of decimals used by the token function decimals(uint256 tokenId_) public view virtual returns (uint8); + + /// @notice Returns the URI of the token + /// + /// @param tokenId_ The ID of the token + /// @return string The URI of the token + function tokenURI(uint256 tokenId_) public view virtual returns (string memory); } diff --git a/src/lib/SVG.sol b/src/lib/SVG.sol index 98054ba3..9b693cca 100644 --- a/src/lib/SVG.sol +++ b/src/lib/SVG.sol @@ -6,33 +6,24 @@ pragma solidity ^0.8.12; // Core utils used extensively to format CSS and numbers. library utils { // used to simulate empty strings - string internal constant NULL = ''; + string internal constant NULL = ""; // formats a CSS variable line. includes a semicolon for formatting. - function setCssVar(string memory _key, string memory _val) - internal - pure - returns (string memory) - { - return string.concat('--', _key, ':', _val, ';'); + function setCssVar( + string memory _key, + string memory _val + ) internal pure returns (string memory) { + return string.concat("--", _key, ":", _val, ";"); } // formats getting a css variable - function getCssVar(string memory _key) - internal - pure - returns (string memory) - { - return string.concat('var(--', _key, ')'); + function getCssVar(string memory _key) internal pure returns (string memory) { + return string.concat("var(--", _key, ")"); } // formats getting a def URL - function getDefURL(string memory _id) - internal - pure - returns (string memory) - { - return string.concat('url(#', _id, ')'); + function getDefURL(string memory _id) internal pure returns (string memory) { + return string.concat("url(#", _id, ")"); } // formats rgba white with a specified opacity / alpha @@ -52,63 +43,53 @@ library utils { uint256 _b, uint256 _a ) internal pure returns (string memory) { - string memory formattedA = _a < 100 - ? string.concat('0.', utils.uint2str(_a)) - : '1'; - return - string.concat( - 'rgba(', - utils.uint2str(_r), - ',', - utils.uint2str(_g), - ',', - utils.uint2str(_b), - ',', - formattedA, - ')' - ); + string memory formattedA = _a < 100 ? string.concat("0.", utils.uint2str(_a)) : "1"; + return string.concat( + "rgba(", + utils.uint2str(_r), + ",", + utils.uint2str(_g), + ",", + utils.uint2str(_b), + ",", + formattedA, + ")" + ); } // checks if two strings are equal - function stringsEqual(string memory _a, string memory _b) - internal - pure - returns (bool) - { - return - keccak256(abi.encodePacked(_a)) == keccak256(abi.encodePacked(_b)); + function stringsEqual(string memory _a, string memory _b) internal pure returns (bool) { + return keccak256(abi.encodePacked(_a)) == keccak256(abi.encodePacked(_b)); } // returns the length of a string in characters - function utfStringLength(string memory _str) - internal - pure - returns (uint256 length) - { + function utfStringLength(string memory _str) internal pure returns (uint256 length) { uint256 i = 0; bytes memory string_rep = bytes(_str); while (i < string_rep.length) { - if (string_rep[i] >> 7 == 0) i += 1; - else if (string_rep[i] >> 5 == bytes1(uint8(0x6))) i += 2; - else if (string_rep[i] >> 4 == bytes1(uint8(0xE))) i += 3; - else if (string_rep[i] >> 3 == bytes1(uint8(0x1E))) + if (string_rep[i] >> 7 == 0) { + i += 1; + } else if (string_rep[i] >> 5 == bytes1(uint8(0x6))) { + i += 2; + } else if (string_rep[i] >> 4 == bytes1(uint8(0xE))) { + i += 3; + } else if (string_rep[i] >> 3 == bytes1(uint8(0x1E))) { i += 4; - //For safety - else i += 1; + } + //For safety + else { + i += 1; + } length++; } } // converts an unsigned integer to a string - function uint2str(uint256 _i) - internal - pure - returns (string memory _uintAsString) - { + function uint2str(uint256 _i) internal pure returns (string memory _uintAsString) { if (_i == 0) { - return '0'; + return "0"; } uint256 j = _i; uint256 len; @@ -133,113 +114,91 @@ library utils { // onchain SVG's with a simple, web-like API. library svg { /* MAIN ELEMENTS */ - function g(string memory _props, string memory _children) - internal - pure - returns (string memory) - { - return el('g', _props, _children); + function g( + string memory _props, + string memory _children + ) internal pure returns (string memory) { + return el("g", _props, _children); } - function path(string memory _props, string memory _children) - internal - pure - returns (string memory) - { - return el('path', _props, _children); + function path( + string memory _props, + string memory _children + ) internal pure returns (string memory) { + return el("path", _props, _children); } - function text(string memory _props, string memory _children) - internal - pure - returns (string memory) - { - return el('text', _props, _children); + function text( + string memory _props, + string memory _children + ) internal pure returns (string memory) { + return el("text", _props, _children); } - function line(string memory _props, string memory _children) - internal - pure - returns (string memory) - { - return el('line', _props, _children); + function line( + string memory _props, + string memory _children + ) internal pure returns (string memory) { + return el("line", _props, _children); } - function circle(string memory _props, string memory _children) - internal - pure - returns (string memory) - { - return el('circle', _props, _children); + function circle( + string memory _props, + string memory _children + ) internal pure returns (string memory) { + return el("circle", _props, _children); } - function circle(string memory _props) - internal - pure - returns (string memory) - { - return el('circle', _props); + function circle(string memory _props) internal pure returns (string memory) { + return el("circle", _props); } - function ellipse(string memory _props, string memory _children) - internal - pure - returns (string memory) - { - return el('ellipse', _props, _children); + function ellipse( + string memory _props, + string memory _children + ) internal pure returns (string memory) { + return el("ellipse", _props, _children); } function ellipse(string memory _props) internal pure returns (string memory) { - return el('ellipse', _props); + return el("ellipse", _props); } - function rect(string memory _props, string memory _children) - internal - pure - returns (string memory) - { - return el('rect', _props, _children); + function rect( + string memory _props, + string memory _children + ) internal pure returns (string memory) { + return el("rect", _props, _children); } - function rect(string memory _props) - internal - pure - returns (string memory) - { - return el('rect', _props); + function rect(string memory _props) internal pure returns (string memory) { + return el("rect", _props); } - function filter(string memory _props, string memory _children) - internal - pure - returns (string memory) - { - return el('filter', _props, _children); + function filter( + string memory _props, + string memory _children + ) internal pure returns (string memory) { + return el("filter", _props, _children); } - function cdata(string memory _content) - internal - pure - returns (string memory) - { - return string.concat('', _content, ''); + function cdata(string memory _content) internal pure returns (string memory) { + return string.concat("", _content, ""); } /* GRADIENTS */ - function radialGradient(string memory _props, string memory _children) - internal - pure - returns (string memory) - { - return el('radialGradient', _props, _children); + function radialGradient( + string memory _props, + string memory _children + ) internal pure returns (string memory) { + return el("radialGradient", _props, _children); } - function linearGradient(string memory _props, string memory _children) - internal - pure - returns (string memory) - { - return el('linearGradient', _props, _children); + function linearGradient( + string memory _props, + string memory _children + ) internal pure returns (string memory) { + return el("linearGradient", _props, _children); } function gradientStop( @@ -247,37 +206,27 @@ library svg { string memory stopColor, string memory _props ) internal pure returns (string memory) { - return - el( - 'stop', - string.concat( - prop('stop-color', stopColor), - ' ', - prop('offset', string.concat(utils.uint2str(offset), '%')), - ' ', - _props - ) - ); + return el( + "stop", + string.concat( + prop("stop-color", stopColor), + " ", + prop("offset", string.concat(utils.uint2str(offset), "%")), + " ", + _props + ) + ); } - function animateTransform(string memory _props) - internal - pure - returns (string memory) - { - return el('animateTransform', _props); + function animateTransform(string memory _props) internal pure returns (string memory) { + return el("animateTransform", _props); } - function image(string memory _href, string memory _props) - internal - pure - returns (string memory) - { - return - el( - 'image', - string.concat(prop('href', _href), ' ', _props) - ); + function image( + string memory _href, + string memory _props + ) internal pure returns (string memory) { + return el("image", string.concat(prop("href", _href), " ", _props)); } /* COMMON */ @@ -287,41 +236,16 @@ library svg { string memory _props, string memory _children ) internal pure returns (string memory) { - return - string.concat( - '<', - _tag, - ' ', - _props, - '>', - _children, - '' - ); + return string.concat("<", _tag, " ", _props, ">", _children, ""); } // A generic element, can be used to construct any SVG (or HTML) element without children - function el( - string memory _tag, - string memory _props - ) internal pure returns (string memory) { - return - string.concat( - '<', - _tag, - ' ', - _props, - '/>' - ); + function el(string memory _tag, string memory _props) internal pure returns (string memory) { + return string.concat("<", _tag, " ", _props, "/>"); } // an SVG attribute - function prop(string memory _key, string memory _val) - internal - pure - returns (string memory) - { - return string.concat(_key, '=', '"', _val, '" '); + function prop(string memory _key, string memory _val) internal pure returns (string memory) { + return string.concat(_key, "=", '"', _val, '" '); } } diff --git a/src/modules/derivatives/DerivativeCardDark.sol b/src/modules/derivatives/DerivativeCardDark.sol deleted file mode 100644 index 7941ce0f..00000000 --- a/src/modules/derivatives/DerivativeCardDark.sol +++ /dev/null @@ -1,465 +0,0 @@ -/// SPDX-License-Identifier: BSL-1.1 -pragma solidity 0.8.19; - -import {svg, utils} from "src/lib/SVG.sol"; - -contract DerivativeCardDark { - - struct Info { - string derivativeType; - string baseAssetSymbol; - string quoteAssetSymbol; - string tokenId; - string tokenAddress; - string erc20Address; - Property[] properties; - } - - struct Property { - string key; - string stringValue; - uint256 value; - } - - struct Colors { - string blue; - string lightBlue; - string green; - string yellowGreen; - string yellow; - string orange; - string red; - } - - string internal constant STROKE = 'stroke="#000" stroke-miterlimit="10" stroke-width="4px" '; - string internal constant TEXT_STYLE= 'font-family="\'Menlo\', monospace" fill="white"'; - - Colors internal colors; - - constructor() { - colors = Colors({ - blue: 'rgb(110, 148, 240)', - lightBlue: 'rgb(118, 189, 242)', - green: 'rgb(206, 244, 117)', - yellowGreen: 'rgb(243, 244, 189)', - yellow: 'rgb(243, 244, 189)', - orange: 'rgb(246, 172, 84)', - red: 'rgb(242, 103, 64)' - }); - } - - function render(Info memory tokenInfo) internal view returns (string memory) { - return string.concat( - '', - svg.el('defs', utils.NULL, string.concat(fullGradient(), fullGradient90(), fullGradientReverse90(), blueGreenGradient(), orangeRedGradient())), - svg.rect( - string.concat( - svg.prop('x', '0'), - svg.prop('y', '0'), - svg.prop('width', '290'), - svg.prop('height', '500'), - svg.prop('fill', 'rgb(30, 30, 30)'), - svg.prop('rx', '25'), - svg.prop('ry', '25') - ), - utils.NULL - ), - svg.rect( - string.concat( - svg.prop('x', '8'), - svg.prop('y', '8'), - svg.prop('width', '274'), - svg.prop('height', '484'), - svg.prop('fill', 'none'), - svg.prop('stroke', "url('#fullGradientReverse90')"), - svg.prop('stroke-width', '2'), - svg.prop('rx', '20'), - svg.prop('ry', '20') - ), - utils.NULL - ), - svg.text( - string.concat( - svg.prop('x', '145'), - svg.prop('y', '40'), - svg.prop('font-size', '20'), - svg.prop('text-anchor', 'middle'), - TEXT_STYLE - ), - tokenInfo.derivativeType - ), - svg.text( - string.concat( - svg.prop('x', '145'), - svg.prop('y', '100'), - svg.prop('font-size', '56'), - svg.prop('text-anchor', 'middle'), - // svg.prop('font-family', 'Menlo, monospace'), - // svg.prop('fill', "url('#orangeRedGradient')") - TEXT_STYLE - ), - tokenInfo.baseAssetSymbol - ), - progressBar(tokenInfo.properties[0].value, tokenInfo.properties[1].value), - svg.text( - string.concat( - svg.prop('x', '60'), - svg.prop('y', '200'), - svg.prop('font-size', '12'), - svg.prop('text-anchor', 'middle'), - TEXT_STYLE - ), - tokenInfo.properties[0].stringValue - ), - svg.text( - string.concat( - svg.prop('x', '230'), - svg.prop('y', '200'), - svg.prop('font-size', '12'), - svg.prop('text-anchor', 'middle'), - TEXT_STYLE - ), - tokenInfo.properties[1].stringValue - ), - logo(), - svg.text( - string.concat( - svg.prop('x', '145'), - svg.prop('y', '460'), - svg.prop('font-size', '10'), - svg.prop('text-anchor', 'middle'), - TEXT_STYLE - ), - tokenInfo.tokenAddress - ), - svg.text( - string.concat( - svg.prop('x', '145'), - svg.prop('y', '480'), - svg.prop('font-size', '10'), - svg.prop('text-anchor', 'middle'), - TEXT_STYLE - ), - string.concat('ID: ', tokenInfo.tokenId) - ), - '' - ); - } - - function logo() internal pure returns (string memory) { - return string.concat( - svg.rect( - string.concat( - svg.prop('x', '143'), - svg.prop('y', '240'), - svg.prop('width', '6'), - svg.prop('height', '125'), - svg.prop('fill', "url('#fullGradientReverse90')") - ), - utils.NULL - ), - svg.rect( - string.concat( - svg.prop('x', '79'), - svg.prop('y', '246'), - svg.prop('width', '6'), - svg.prop('height', '125'), - svg.prop('fill', "url('#fullGradient90')"), - svg.prop('transform', 'rotate(-60 145 250)') - ), - utils.NULL - ), - svg.rect( - string.concat( - svg.prop('x', '206'), - svg.prop('y', '244'), - svg.prop('width', '6'), - svg.prop('height', '125'), - svg.prop('fill', "url('#fullGradient90')"), - svg.prop('transform', 'rotate(60 145 250)') - ), - utils.NULL - ) - ); - } - - function fullGradientStops() internal view returns (string memory) { - return string.concat( - svg.gradientStop( - 2, - colors.blue, - utils.NULL - ), - svg.gradientStop( - 10, - colors.lightBlue, - utils.NULL - ), - svg.gradientStop( - 32, - colors.green, - utils.NULL - ), - svg.gradientStop( - 49, - colors.yellowGreen, - utils.NULL - ), - svg.gradientStop( - 52, - colors.yellow, - utils.NULL - ), - svg.gradientStop( - 79, - colors.orange, - utils.NULL - ), - svg.gradientStop( - 100, - colors.red, - utils.NULL - ) - ); - } - - function fullGradientReverseStops() internal view returns (string memory) { - return string.concat( - svg.gradientStop( - 2, - colors.red, - utils.NULL - ), - svg.gradientStop( - 21, - colors.orange, - utils.NULL - ), - svg.gradientStop( - 48, - colors.yellow, - utils.NULL - ), - svg.gradientStop( - 51, - colors.yellowGreen, - utils.NULL - ), - svg.gradientStop( - 68, - colors.green, - utils.NULL - ), - svg.gradientStop( - 90, - colors.lightBlue, - utils.NULL - ), - svg.gradientStop( - 100, - colors.blue, - utils.NULL - ) - ); - } - - function fullGradient() internal view returns (string memory) { - return svg.linearGradient( - string.concat( - svg.prop('id', 'fullGradient') - ), - fullGradientStops() - ); - } - - function fullGradient90() internal view returns (string memory) { - return svg.linearGradient( - string.concat( - svg.prop('id', 'fullGradient90'), - svg.prop('gradientTransform', 'rotate(90)') - ), - fullGradientStops() - ); - } - - function fullGradientReverse90() internal view returns (string memory) { - return svg.linearGradient( - string.concat( - svg.prop('id', 'fullGradientReverse90'), - svg.prop('gradientTransform', 'rotate(90)') - ), - fullGradientReverseStops() - ); - } - - function blueGreenGradient() internal view returns (string memory) { - return svg.linearGradient( - string.concat( - svg.prop('id', 'blueGreenGradient'), - svg.prop('gradientUnits', 'userSpaceOnUse') - ), - string.concat( - svg.gradientStop( - 20, - colors.blue, - utils.NULL - ), - svg.gradientStop( - 40, - colors.lightBlue, - utils.NULL - ), - svg.gradientStop( - 60, - colors.green, - utils.NULL - ) - ) - ); - } - - function orangeRedGradient() internal view returns (string memory) { - return svg.linearGradient( - string.concat( - svg.prop('id', 'orangeRedGradient'), - svg.prop('gradientTransform', 'rotate(90)') - ), - string.concat( - svg.gradientStop( - 30, - colors.orange, - utils.NULL - ), - svg.gradientStop( - 90, - colors.red, - utils.NULL - ) - ) - ); - } - - function progressBar(uint256 start, uint256 end) internal view returns (string memory) { - uint256 currentTime = 1717200000 + 70 * 86400; // block.timestamp; - - bool started = start <= currentTime; - - uint256 progress = started ? (currentTime - start) * 100 / (end - start) : 0; - // progress can be at most 100 - progress = progress > 100 ? 100 : progress; - - uint256 len = (168 * progress) / 100; - - string memory startBar = svg.line( - string.concat( - svg.prop('x1', '60'), - svg.prop('y1', '155'), - svg.prop('x2', '60'), - svg.prop('y2', '175'), - svg.prop('stroke', started ? colors.blue : 'grey'), - svg.prop('stroke-width', '6') - ), - utils.NULL - ); - - string memory endBar = svg.line( - string.concat( - svg.prop('x1', '230'), - svg.prop('y1', '155'), - svg.prop('x2', '230'), - svg.prop('y2', '175'), - svg.prop('stroke', progress == 100 ? colors.green : 'grey'), - svg.prop('stroke-width', '6') - ), - utils.NULL - ); - - - string memory progressLine = svg.line( - string.concat( - svg.prop('x1', '62'), - svg.prop('y1', '165'), - svg.prop('x2', utils.uint2str(62 + len)), - svg.prop('y2', '165'), - svg.prop('stroke', "url('#blueGreenGradient')"), - svg.prop('stroke-width', '6') - ), - utils.NULL - ); - - string memory animateLine = svg.rect( - string.concat( - svg.prop('x', '62'), - svg.prop('y', '161'), - svg.prop('width', '16'), - svg.prop('height', '8'), - svg.prop('fill', "url('#blueGreenGradient')"), - svg.prop('rx', '4'), - svg.prop('ry', '4') - ), - svg.el('animate', - string.concat( - svg.prop('attributeName', 'x'), - svg.prop('values', string.concat('62;', utils.uint2str(62 + len - 16), ';')), - svg.prop('dur', '3s'), - svg.prop('repeatCount', 'indefinite') - ), - utils.NULL - ) - ); - - string memory shadowLine = svg.line( - string.concat( - svg.prop('x1', '63'), - svg.prop('y1', '165'), - svg.prop('x2', '230'), - svg.prop('y2', '165'), - svg.prop('stroke', 'grey'), - svg.prop('stroke-width', '4') - ), - utils.NULL - ); - - string memory progressCircle = svg.circle( - string.concat( - svg.prop('cx', utils.uint2str(62 + len)), - svg.prop('cy', '165'), - svg.prop('r', '6'), - svg.prop('fill', "url('#blueGreenGradient')") - ), - utils.NULL - ); - - return svg.g( - utils.NULL, - string.concat( - startBar, - shadowLine, - progressLine, - progress < 5 ? '' : animateLine, - endBar, - progress < 5 || progress > 95 ? '' : progressCircle - ) - ); - } - - function example() external view returns (string memory) { - Property[] memory properties = new Property[](2); - properties[0] = Property({key: "Vesting Start", stringValue: "2024-06-01", value: 1717200000}); - properties[1] = Property({key: "Vesting End", stringValue: "2024-09-01", value: 1725148800}); - - - Info memory info = Info({ - derivativeType: "Linear Vesting", - baseAssetSymbol: "XYZ", - quoteAssetSymbol: "", - tokenId: "123456", - tokenAddress: "0x1234567890123456789012345678901234567890", - erc20Address: "0xfedcba0987654321fedcba0987654321fedcba09", - properties: properties - }); - - return render(info); - } - -} \ No newline at end of file diff --git a/src/modules/derivatives/DerivativeCardGradient.sol b/src/modules/derivatives/DerivativeCardGradient.sol deleted file mode 100644 index bdd5bcc9..00000000 --- a/src/modules/derivatives/DerivativeCardGradient.sol +++ /dev/null @@ -1,384 +0,0 @@ -/// SPDX-License-Identifier: BSL-1.1 -pragma solidity 0.8.19; - -import {svg, utils} from "src/lib/SVG.sol"; - -contract DerivativeCardDark { - - struct Info { - string derivativeType; - string baseAssetSymbol; - string quoteAssetSymbol; - Property[] properties; - } - - struct Property { - string key; - string value; - } - - struct Colors { - string blue; - string lightBlue; - string green; - string yellowGreen; - string yellow; - string orange; - string red; - } - - string internal constant STROKE = 'stroke="#000" stroke-miterlimit="10" stroke-width="4px" '; - string internal constant TEXT_STYLE= 'font-family="\'Menlo\', monospace" fill="black"'; - - Colors internal colors; - - constructor() { - colors = Colors({ - blue: 'rgb(110, 148, 240)', - lightBlue: 'rgb(118, 189, 242)', - green: 'rgb(206, 244, 117)', - yellowGreen: 'rgb(243, 244, 189)', - yellow: 'rgb(243, 244, 189)', - orange: 'rgb(246, 172, 84)', - red: 'rgb(242, 103, 64)' - }); - } - - function render(Info memory tokenInfo) internal view returns (string memory) { - return string.concat( - '', - svg.el('defs', utils.NULL, string.concat(fullGradient(), fullVertGradient(), blueGreenGradient(), orangeRedGradient())), - svg.rect( - string.concat( - svg.prop('x', '5'), - svg.prop('y', '5'), - svg.prop('width', '280'), - svg.prop('height', '490'), - svg.prop('fill', "url('#orangeRedGradient')"), - svg.prop('stroke', 'none'), - svg.prop('rx', '20'), - svg.prop('ry', '20') - ), - utils.NULL - ), - svg.rect( - string.concat( - svg.prop('x', '15'), - svg.prop('y', '15'), - svg.prop('width', '260'), - svg.prop('height', '470'), - svg.prop('fill', 'none'), - svg.prop('stroke', 'black'), - svg.prop('rx', '20'), - svg.prop('ry', '20') - ), - utils.NULL - ), - svg.text( - string.concat( - svg.prop('x', '145'), - svg.prop('y', '45'), - svg.prop('font-size', '20'), - svg.prop('text-anchor', 'middle'), - TEXT_STYLE - ), - tokenInfo.derivativeType - ), - svg.text( - string.concat( - svg.prop('x', '145'), - svg.prop('y', '100'), - svg.prop('font-size', '56'), - svg.prop('text-anchor', 'middle'), - TEXT_STYLE - ), - tokenInfo.baseAssetSymbol - ), - progressBar(80), - svg.g( - string.concat( - svg.prop('transform', 'translate(96, 200) scale(0.1)') - ), - logo() - ), - svg.text( - string.concat( - svg.prop('x', '20'), - svg.prop('y', '350'), - svg.prop('font-size', '16'), - svg.prop('text-anchor', 'left'), - TEXT_STYLE - ), - string.concat( - tokenInfo.properties[0].key, - ': ', - tokenInfo.properties[0].value - ) - ), - svg.text( - string.concat( - svg.prop('x', '20'), - svg.prop('y', '380'), - svg.prop('font-size', '16'), - svg.prop('text-anchor', 'left'), - TEXT_STYLE - ), - string.concat( - tokenInfo.properties[1].key, - ': ', - tokenInfo.properties[1].value - ) - ), - // svg.g( - // string.concat( - // svg.prop('transform', 'translate(80, 430) scale(0.05)') - // ), - // wordmark() - // ), - '' - ); - } - - function logo() internal pure returns (string memory) { - return svg.path( - string.concat( - svg.prop('fill' , 'white'), - STROKE, - svg.prop('d', 'M0.34668 818.666L20.3467 852.666L474.347 590.666V838.666H513.347V590.666L966.347 852.666L986.347 818.666L532.347 556.666L746.347 433.666L726.347 399.666L513.347 522.666L514.347 0.665527H474.347V522.666L260.347 399.666L240.347 433.666L454.347 556.666L0.34668 818.666Z') - ), - utils.NULL - ); - } - - function wordmark() internal pure returns (string memory) { - return svg.path( - string.concat( - svg.prop('fill' , "url('#fullGradient')"), - STROKE, - svg.prop('d', 'M2078.65 610.666H2118.65C2118.65 720.666 2205.65 799.666 2327.65 799.666C2439.65 799.666 2528.65 738.666 2528.65 629.666C2528.65 526.666 2460.65 465.666 2318.65 428.666C2167.65 397.666 2090.65 318.666 2090.65 201.666C2090.65 84.6656 2204.65 -2.33439 2323.65 0.665612C2450.65 -2.33439 2559.65 90.6656 2559.65 223.666H2521.65C2521.65 112.666 2432.65 40.6656 2323.65 40.6656C2223.65 40.6656 2130.65 110.666 2130.65 201.666C2130.65 300.666 2200.65 360.666 2331.65 394.666C2492.65 431.666 2569.65 505.666 2569.65 624.666C2569.65 743.666 2461.65 839.666 2323.65 839.666C2185.65 839.666 2078.65 742.666 2078.65 610.666ZM1926.65 820.666V20.6656H1966.65V820.666H1926.65ZM1005.65 801.666L1035.65 831.666L1416.65 450.666L1797.65 830.666L1826.65 800.666L1445.65 419.666L1826.65 40.6656L1796.65 10.6656L1415.65 391.666L1034.65 11.6656L1005.65 41.6656L1386.65 419.666L1005.65 801.666ZM45.6533 419.666C45.6533 627.666 212.653 794.666 419.653 794.666C611.653 794.666 793.653 648.666 793.653 419.666C793.653 190.666 611.653 45.6656 419.653 45.6656C212.653 45.6656 45.6533 213.666 45.6533 419.666ZM0.65332 419.666C0.65332 188.666 188.653 0.665612 419.653 0.665612C582.653 0.665612 761.653 103.666 817.653 299.666L869.653 20.6656H910.653L833.653 419.666L910.653 820.666H869.653L817.653 540.666C761.653 736.666 582.653 839.666 419.653 839.666C188.653 839.666 0.65332 651.666 0.65332 419.666Z') - ), - utils.NULL - ); - } - - function fullGradient() internal view returns (string memory) { - return svg.linearGradient( - string.concat( - svg.prop('id', 'fullGradient') - ), - string.concat( - svg.gradientStop( - 2, - colors.blue, - utils.NULL - ), - svg.gradientStop( - 10, - colors.lightBlue, - utils.NULL - ), - svg.gradientStop( - 32, - colors.green, - utils.NULL - ), - svg.gradientStop( - 49, - colors.yellowGreen, - utils.NULL - ), - svg.gradientStop( - 52, - colors.yellow, - utils.NULL - ), - svg.gradientStop( - 79, - colors.orange, - utils.NULL - ), - svg.gradientStop( - 100, - colors.red, - utils.NULL - ) - ) - ); - } - - function fullVertGradient() internal view returns (string memory) { - return svg.linearGradient( - string.concat( - svg.prop('id', 'fullVertGradient'), - svg.prop('gradientTransform', 'rotate(90)') - ), - string.concat( - svg.gradientStop( - 8, - colors.blue, - utils.NULL - ), - svg.gradientStop( - 20, - colors.lightBlue, - utils.NULL - ), - svg.gradientStop( - 42, - colors.green, - utils.NULL - ), - svg.gradientStop( - 59, - colors.yellowGreen, - utils.NULL - ), - svg.gradientStop( - 65, - colors.yellow, - utils.NULL - ), - svg.gradientStop( - 79, - colors.orange, - utils.NULL - ), - svg.gradientStop( - 100, - colors.red, - utils.NULL - ) - ) - ); - } - - function blueGreenGradient() internal view returns (string memory) { - return svg.linearGradient( - string.concat( - svg.prop('id', 'blueGreenGradient'), - svg.prop('gradientUnits', 'userSpaceOnUse') - ), - string.concat( - svg.gradientStop( - 20, - colors.blue, - utils.NULL - ), - svg.gradientStop( - 40, - colors.lightBlue, - utils.NULL - ), - svg.gradientStop( - 60, - colors.green, - utils.NULL - ) - ) - ); - } - - function orangeRedGradient() internal view returns (string memory) { - return svg.linearGradient( - string.concat( - svg.prop('id', 'orangeRedGradient'), - svg.prop('gradientTransform', 'rotate(60)') - ), - string.concat( - svg.gradientStop( - 30, - colors.orange, - utils.NULL - ), - svg.gradientStop( - 90, - colors.red, - utils.NULL - ) - ) - ); - } - - function progressBar(uint256 _progress) internal view returns (string memory) { - - uint256 len = 168 * _progress / 100; - - string memory startBar = svg.line( - string.concat( - svg.prop('x1', '60'), - svg.prop('y1', '135'), - svg.prop('x2', '60'), - svg.prop('y2', '155'), - svg.prop('stroke', colors.blue), - svg.prop('stroke-width', '6') - ), - utils.NULL - ); - - string memory endBarColor = _progress == 100 ? colors.green : 'grey'; - - string memory endBar = svg.line( - string.concat( - svg.prop('x1', '230'), - svg.prop('y1', '135'), - svg.prop('x2', '230'), - svg.prop('y2', '155'), - svg.prop('stroke', endBarColor), - svg.prop('stroke-width', '6') - ), - utils.NULL - ); - - - string memory progressLine = svg.line( - string.concat( - svg.prop('x1', '63'), - svg.prop('y1', '145'), - svg.prop('x2', utils.uint2str(62 + len)), - svg.prop('y2', '145'), - svg.prop('stroke', "url('#blueGreenGradient')"), - svg.prop('stroke-width', '6') - ), - utils.NULL - ); - - string memory shadowLine = svg.line( - string.concat( - svg.prop('x1', '63'), - svg.prop('y1', '145'), - svg.prop('x2', '230'), - svg.prop('y2', '145'), - svg.prop('stroke', 'grey'), - svg.prop('stroke-width', '4') - ), - utils.NULL - ); - - return svg.g( - utils.NULL, - string.concat( - startBar, - shadowLine, - progressLine, - endBar - ) - ); - } - - function example() external view returns (string memory) { - Property[] memory properties = new Property[](2); - properties[0] = Property({key: "Vesting Start", value: "2024-07-01"}); - properties[1] = Property({key: "Vesting End", value: "2024-09-01"}); - - - Info memory info = Info({ - derivativeType: "Linear Vesting", - baseAssetSymbol: "XYZ", - quoteAssetSymbol: "", - properties: properties - }); - - return render(info); - } - -} \ No newline at end of file diff --git a/src/modules/derivatives/DerivativeCardLight.sol b/src/modules/derivatives/DerivativeCardLight.sol deleted file mode 100644 index b4c116d1..00000000 --- a/src/modules/derivatives/DerivativeCardLight.sol +++ /dev/null @@ -1,371 +0,0 @@ -/// SPDX-License-Identifier: BSL-1.1 -pragma solidity 0.8.19; - -import {svg, utils} from "src/lib/SVG.sol"; - -contract DerivativeCardDark { - - struct Info { - string derivativeType; - string baseAssetSymbol; - string quoteAssetSymbol; - Property[] properties; - } - - struct Property { - string key; - string value; - } - - struct Colors { - string blue; - string lightBlue; - string green; - string yellowGreen; - string yellow; - string orange; - string red; - } - - string internal constant STROKE = 'stroke="#000" stroke-miterlimit="10" stroke-width="4px" '; - string internal constant TEXT_STYLE= 'font-family="\'Menlo\', monospace" fill="#000"'; - - Colors internal colors; - - constructor() { - colors = Colors({ - blue: 'rgb(110, 148, 240)', - lightBlue: 'rgb(118, 189, 242)', - green: 'rgb(206, 244, 117)', - yellowGreen: 'rgb(243, 244, 189)', - yellow: 'rgb(243, 244, 189)', - orange: 'rgb(246, 172, 84)', - red: 'rgb(242, 103, 64)' - }); - } - - function render(Info memory tokenInfo) internal view returns (string memory) { - return string.concat( - '', - svg.el('defs', utils.NULL, string.concat(fullGradient(), fullVertGradient(), blueGreenGradient(), orangeRedGradient())), - svg.rect( - string.concat( - svg.prop('x', '5'), - svg.prop('y', '5'), - svg.prop('width', '280'), - svg.prop('height', '490'), - svg.prop('fill', 'rgb(200, 200, 200)'), - svg.prop('stroke', 'black'), - svg.prop('rx', '10'), - svg.prop('ry', '10') - ), - utils.NULL - ), - svg.text( - string.concat( - svg.prop('x', '145'), - svg.prop('y', '30'), - svg.prop('font-size', '20'), - svg.prop('text-anchor', 'middle'), - TEXT_STYLE - ), - tokenInfo.derivativeType - ), - svg.text( - string.concat( - svg.prop('x', '145'), - svg.prop('y', '90'), - svg.prop('font-size', '56'), - svg.prop('text-anchor', 'middle'), - TEXT_STYLE - ), - tokenInfo.baseAssetSymbol - ), - progressBar(60), - svg.g( - string.concat( - svg.prop('transform', 'translate(96, 200) scale(0.1)') - ), - logo() - ), - svg.text( - string.concat( - svg.prop('x', '20'), - svg.prop('y', '350'), - svg.prop('font-size', '16'), - svg.prop('text-anchor', 'left'), - TEXT_STYLE - ), - string.concat( - tokenInfo.properties[0].key, - ': ', - tokenInfo.properties[0].value - ) - ), - svg.text( - string.concat( - svg.prop('x', '20'), - svg.prop('y', '380'), - svg.prop('font-size', '16'), - svg.prop('text-anchor', 'left'), - TEXT_STYLE - ), - string.concat( - tokenInfo.properties[1].key, - ': ', - tokenInfo.properties[1].value - ) - ), - // svg.g( - // string.concat( - // svg.prop('transform', 'translate(80, 430) scale(0.05)') - // ), - // wordmark() - // ), - '' - ); - } - - function logo() internal pure returns (string memory) { - return svg.path( - string.concat( - svg.prop('fill' , "url('#fullVertGradient')"), - // STROKE, - svg.prop('d', 'M0.34668 818.666L20.3467 852.666L474.347 590.666V838.666H513.347V590.666L966.347 852.666L986.347 818.666L532.347 556.666L746.347 433.666L726.347 399.666L513.347 522.666L514.347 0.665527H474.347V522.666L260.347 399.666L240.347 433.666L454.347 556.666L0.34668 818.666Z') - ), - utils.NULL - ); - } - - function wordmark() internal pure returns (string memory) { - return svg.path( - string.concat( - svg.prop('fill' , "url('#fullGradient')"), - STROKE, - svg.prop('d', 'M2078.65 610.666H2118.65C2118.65 720.666 2205.65 799.666 2327.65 799.666C2439.65 799.666 2528.65 738.666 2528.65 629.666C2528.65 526.666 2460.65 465.666 2318.65 428.666C2167.65 397.666 2090.65 318.666 2090.65 201.666C2090.65 84.6656 2204.65 -2.33439 2323.65 0.665612C2450.65 -2.33439 2559.65 90.6656 2559.65 223.666H2521.65C2521.65 112.666 2432.65 40.6656 2323.65 40.6656C2223.65 40.6656 2130.65 110.666 2130.65 201.666C2130.65 300.666 2200.65 360.666 2331.65 394.666C2492.65 431.666 2569.65 505.666 2569.65 624.666C2569.65 743.666 2461.65 839.666 2323.65 839.666C2185.65 839.666 2078.65 742.666 2078.65 610.666ZM1926.65 820.666V20.6656H1966.65V820.666H1926.65ZM1005.65 801.666L1035.65 831.666L1416.65 450.666L1797.65 830.666L1826.65 800.666L1445.65 419.666L1826.65 40.6656L1796.65 10.6656L1415.65 391.666L1034.65 11.6656L1005.65 41.6656L1386.65 419.666L1005.65 801.666ZM45.6533 419.666C45.6533 627.666 212.653 794.666 419.653 794.666C611.653 794.666 793.653 648.666 793.653 419.666C793.653 190.666 611.653 45.6656 419.653 45.6656C212.653 45.6656 45.6533 213.666 45.6533 419.666ZM0.65332 419.666C0.65332 188.666 188.653 0.665612 419.653 0.665612C582.653 0.665612 761.653 103.666 817.653 299.666L869.653 20.6656H910.653L833.653 419.666L910.653 820.666H869.653L817.653 540.666C761.653 736.666 582.653 839.666 419.653 839.666C188.653 839.666 0.65332 651.666 0.65332 419.666Z') - ), - utils.NULL - ); - } - - function fullGradient() internal view returns (string memory) { - return svg.linearGradient( - string.concat( - svg.prop('id', 'fullGradient') - ), - string.concat( - svg.gradientStop( - 2, - colors.blue, - utils.NULL - ), - svg.gradientStop( - 10, - colors.lightBlue, - utils.NULL - ), - svg.gradientStop( - 32, - colors.green, - utils.NULL - ), - svg.gradientStop( - 49, - colors.yellowGreen, - utils.NULL - ), - svg.gradientStop( - 52, - colors.yellow, - utils.NULL - ), - svg.gradientStop( - 79, - colors.orange, - utils.NULL - ), - svg.gradientStop( - 100, - colors.red, - utils.NULL - ) - ) - ); - } - - function fullVertGradient() internal view returns (string memory) { - return svg.linearGradient( - string.concat( - svg.prop('id', 'fullVertGradient'), - svg.prop('gradientTransform', 'rotate(90)') - ), - string.concat( - svg.gradientStop( - 8, - colors.blue, - utils.NULL - ), - svg.gradientStop( - 20, - colors.lightBlue, - utils.NULL - ), - svg.gradientStop( - 42, - colors.green, - utils.NULL - ), - svg.gradientStop( - 59, - colors.yellowGreen, - utils.NULL - ), - svg.gradientStop( - 65, - colors.yellow, - utils.NULL - ), - svg.gradientStop( - 79, - colors.orange, - utils.NULL - ), - svg.gradientStop( - 100, - colors.red, - utils.NULL - ) - ) - ); - } - - function blueGreenGradient() internal view returns (string memory) { - return svg.linearGradient( - string.concat( - svg.prop('id', 'blueGreenGradient'), - svg.prop('gradientUnits', 'userSpaceOnUse') - ), - string.concat( - svg.gradientStop( - 20, - colors.blue, - utils.NULL - ), - svg.gradientStop( - 40, - colors.lightBlue, - utils.NULL - ), - svg.gradientStop( - 60, - colors.green, - utils.NULL - ) - ) - ); - } - - function orangeRedGradient() internal view returns (string memory) { - return svg.linearGradient( - string.concat( - svg.prop('id', 'orangeRedGradient'), - svg.prop('gradientTransform', 'rotate(90)') - ), - string.concat( - svg.gradientStop( - 30, - colors.orange, - utils.NULL - ), - svg.gradientStop( - 90, - colors.red, - utils.NULL - ) - ) - ); - } - - function progressBar(uint256 _progress) internal view returns (string memory) { - - uint256 len = 168 * _progress / 100; - - string memory startBar = svg.line( - string.concat( - svg.prop('x1', '60'), - svg.prop('y1', '135'), - svg.prop('x2', '60'), - svg.prop('y2', '155'), - svg.prop('stroke', colors.blue), - svg.prop('stroke-width', '6') - ), - utils.NULL - ); - - string memory endBarColor = _progress == 100 ? colors.green : 'grey'; - - string memory endBar = svg.line( - string.concat( - svg.prop('x1', '230'), - svg.prop('y1', '135'), - svg.prop('x2', '230'), - svg.prop('y2', '155'), - svg.prop('stroke', endBarColor), - svg.prop('stroke-width', '6') - ), - utils.NULL - ); - - - string memory progressLine = svg.line( - string.concat( - svg.prop('x1', '63'), - svg.prop('y1', '145'), - svg.prop('x2', utils.uint2str(62 + len)), - svg.prop('y2', '145'), - svg.prop('stroke', "url('#blueGreenGradient')"), - svg.prop('stroke-width', '6') - ), - utils.NULL - ); - - string memory shadowLine = svg.line( - string.concat( - svg.prop('x1', '63'), - svg.prop('y1', '145'), - svg.prop('x2', '230'), - svg.prop('y2', '145'), - svg.prop('stroke', 'grey'), - svg.prop('stroke-width', '4') - ), - utils.NULL - ); - - return svg.g( - utils.NULL, - string.concat( - startBar, - shadowLine, - progressLine, - endBar - ) - ); - } - - function example() external view returns (string memory) { - Property[] memory properties = new Property[](2); - properties[0] = Property({key: "Vesting Start", value: "2024-07-01"}); - properties[1] = Property({key: "Vesting End", value: "2024-09-01"}); - - - Info memory info = Info({ - derivativeType: "Linear Vesting Token", - baseAssetSymbol: "XYZ", - quoteAssetSymbol: "", - properties: properties - }); - - return render(info); - } - -} \ No newline at end of file diff --git a/src/modules/derivatives/LinearVesting.sol b/src/modules/derivatives/LinearVesting.sol index a8747b77..9056b00e 100644 --- a/src/modules/derivatives/LinearVesting.sol +++ b/src/modules/derivatives/LinearVesting.sol @@ -14,6 +14,7 @@ import {ILinearVesting} from "src/interfaces/modules/derivatives/ILinearVesting. import {DerivativeModule} from "src/modules/Derivative.sol"; import {Module, Veecode, toKeycode, wrapVeecode} from "src/modules/Modules.sol"; import {SoulboundCloneERC20} from "src/modules/derivatives/SoulboundCloneERC20.sol"; +import {LinearVestingCard} from "src/modules/derivatives/LinearVestingCard.sol"; /// @title LinearVesting /// @notice A derivative module that allows for the creation of linearly vesting tokens @@ -23,7 +24,7 @@ import {SoulboundCloneERC20} from "src/modules/derivatives/SoulboundCloneERC20.s /// /// The start timestamp enables vesting tokens to have a cliff, after which vesting commences. /// @author Axis Finance -contract LinearVesting is DerivativeModule, ILinearVesting { +contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { using SafeTransferLib for ERC20; using ClonesWithImmutableArgs for address; using Timestamp for uint48; @@ -42,6 +43,7 @@ contract LinearVesting is DerivativeModule, ILinearVesting { // ========== MODULE SETUP ========== // constructor(address parent_) Module(parent_) { + // LinearVestingCard() // Deploy the clone implementation _IMPLEMENTATION = address(new SoulboundCloneERC20()); } @@ -682,4 +684,42 @@ contract LinearVesting is DerivativeModule, ILinearVesting { return ERC20(token.underlyingToken).decimals(); } + + // ========== ERC6909 CONTENT EXTENSION ========== // + + /// @inheritdoc ERC6909Metadata + /// @dev This function reverts if: + /// - The token ID does not exist + function tokenURI(uint256 tokenId_) + public + view + override + onlyValidTokenId(tokenId_) + returns (string memory) + { + Token storage token = tokenMetadata[tokenId_]; + VestingParams memory data = abi.decode(token.data, (VestingParams)); + + // Get the underlying token symbol + string memory _symbol = ERC20(token.underlyingToken).symbol(); + + // Create token info for rendering token card + Info memory info = Info({ + tokenId: tokenId_, + baseAssetSymbol: _symbol, + start: data.start, + expiry: data.expiry + }); + + // Return the token URI + return string.concat( + '{"name": "', + name(tokenId_), + '", "description": "Linear Vesting ', + _symbol, + '", "image": "data:image/svg+xml;utf8,', + render(info), + '"}' + ); + } } diff --git a/src/modules/derivatives/LinearVestingCard.sol b/src/modules/derivatives/LinearVestingCard.sol new file mode 100644 index 00000000..b2f10c2d --- /dev/null +++ b/src/modules/derivatives/LinearVestingCard.sol @@ -0,0 +1,553 @@ +/// SPDX-License-Identifier: BSL-1.1 +pragma solidity 0.8.19; + +import {svg} from "src/lib/SVG.sol"; +import {Timestamp} from "src/lib/Timestamp.sol"; +// import {LibString} from "lib/solady/src/utils/LibString.sol"; +import {Strings as LibString} from "lib/openzeppelin-contracts/contracts/utils/Strings.sol"; + +contract LinearVestingCard { + // ========== DATA STRUCTURES ========== // + + struct Info { + uint256 tokenId; + string baseAssetSymbol; + uint48 start; + uint48 expiry; + } + + struct Colors { + string blue; + string lightBlue; + string green; + string yellowGreen; + string yellow; + string orange; + string red; + } + + // ========== STATE VARIABLES ========== // + + string internal constant TEXT_STYLE = + 'font-family="\'Menlo\', monospace" fill="white" text-anchor="middle"'; + string internal constant NULL = ""; + string internal ADDR_STRING; + + Colors internal colors; + + // ========== CONSTRUCTOR ========== // + + constructor() { + colors = Colors({ + blue: "rgb(110, 148, 240)", + lightBlue: "rgb(118, 189, 242)", + green: "rgb(206, 244, 117)", + yellowGreen: "rgb(243, 244, 189)", + yellow: "rgb(243, 244, 189)", + orange: "rgb(246, 172, 84)", + red: "rgb(242, 103, 64)" + }); + + ADDR_STRING = LibString.toHexString(address(this)); + } + + // ========== RENDERER ========== // + function render(Info memory tokenInfo) internal view returns (string memory) { + return string.concat( + '', + svg.el( + "defs", + NULL, + string.concat( + fullGradient(), fullGradient90(), fullGradientReverse90(), blueGreenGradient() + ) + ), + svg.rect( + // string.concat( + // svg.prop('x', '0'), + // svg.prop('y', '0'), + // svg.prop('width', '290'), + // svg.prop('height', '500'), + // svg.prop('fill', 'rgb(30, 30, 30)'), + // svg.prop('rx', '25'), + // svg.prop('ry', '25') + // ), + 'x="0" y="0" width="290" height="500" fill="rgb(30, 30, 30)" rx="25" ry="25"', + NULL + ), + svg.rect( + // string.concat( + // svg.prop('x', '8'), + // svg.prop('y', '8'), + // svg.prop('width', '274'), + // svg.prop('height', '484'), + // svg.prop('fill', 'none'), + // svg.prop('stroke', "url('#fullGradient')"), + // svg.prop('stroke-width', '2'), + // svg.prop('rx', '20'), + // svg.prop('ry', '20') + // ), + 'x="8" y="8" width="274" height="484" fill="none" stroke="url(#fullGradient)" stroke-width="2" rx="20" ry="20"', + NULL + ), + title(tokenInfo.baseAssetSymbol), + progressBar(uint256(tokenInfo.start), uint256(tokenInfo.expiry)), + progressLabels(tokenInfo.start, tokenInfo.expiry), + logo(), + identifier(tokenInfo.tokenId), + "" + ); + } + + // ========== COMPONENTS ========== // + + function title(string memory symbol) internal pure returns (string memory) { + return string.concat( + svg.text( + // string.concat( + // svg.prop('x', '145'), + // svg.prop('y', '40'), + // svg.prop('font-size', '20'), + // TEXT_STYLE + // ), + string.concat('x="145" y="40" font-size="20" ', TEXT_STYLE), + "Linear Vesting" + ), + svg.text( + // string.concat( + // svg.prop('x', '145'), + // svg.prop('y', '100'), + // svg.prop('font-size', '56'), + // TEXT_STYLE + // ), + string.concat('x="145" y="100" font-size="56" ', TEXT_STYLE), + symbol + ) + ); + } + + function logo() internal pure returns (string memory) { + return string.concat( + svg.rect( + // string.concat( + // svg.prop('x', '143'), + // svg.prop('y', '240'), + // svg.prop('width', '6'), + // svg.prop('height', '125'), + // svg.prop('fill', "url('#fullGradientReverse90')") + // ), + 'x="143" y="240" width="6" height="125" fill="url(#fullGradientReverse90)"', + NULL + ), + svg.rect( + // string.concat( + // svg.prop('x', '79'), + // svg.prop('y', '246'), + // svg.prop('width', '6'), + // svg.prop('height', '125'), + // svg.prop('fill', "url('#fullGradient90')"), + // svg.prop('transform', 'rotate(-60 145 250)') + // ), + 'x="79" y="246" width="6" height="125" fill="url(#fullGradient90)" transform="rotate(-60 145 250)"', + NULL + ), + svg.rect( + // string.concat( + // svg.prop('x', '206'), + // svg.prop('y', '244'), + // svg.prop('width', '6'), + // svg.prop('height', '125'), + // svg.prop('fill', "url('#fullGradient90')"), + // svg.prop('transform', 'rotate(60 145 250)') + // ), + 'x="206" y="244" width="6" height="125" fill="url(#fullGradient90)" transform="rotate(60 145 250)"', + NULL + ) + ); + } + + function identifier(uint256 tokenId) internal view returns (string memory) { + return string.concat( + svg.text( + // string.concat( + // svg.prop('x', '145'), + // svg.prop('y', '460'), + // svg.prop('font-size', '10'), + // TEXT_STYLE + // ), + string.concat('x="145" y="460" font-size="10" ', TEXT_STYLE), + ADDR_STRING + ), + svg.text( + // string.concat( + // svg.prop('x', '145'), + // svg.prop('y', '480'), + // svg.prop('font-size', '10'), + // TEXT_STYLE + // ), + string.concat('x="145" y="480" font-size="10" ', TEXT_STYLE), + string.concat("ID: ", LibString.toString(tokenId)) + ) + ); + } + + function progressBar(uint256 start, uint256 end) internal view returns (string memory) { + uint256 currentTime = block.timestamp; // 1717200000 + 70 * 86400; + + string memory startBar; + string memory endBar; + uint256 progress; + { + bool started = start <= currentTime; + + progress = started ? (currentTime - start) * 100 / (end - start) : 0; + // progress can be at most 100 + progress = progress > 100 ? 100 : progress; + + startBar = svg.line( + // string.concat( + // svg.prop('x1', '60'), + // svg.prop('y1', '155'), + // svg.prop('x2', '60'), + // svg.prop('y2', '175'), + // svg.prop('stroke', started ? colors.blue : 'grey'), + // svg.prop('stroke-width', '6') + // ), + string.concat( + 'x1="60" y1="155" x2="60" y2="175" stroke="', + started ? colors.blue : "grey", + '" stroke-width="6"' + ), + NULL + ); + + endBar = svg.line( + // string.concat( + // svg.prop('x1', '230'), + // svg.prop('y1', '155'), + // svg.prop('x2', '230'), + // svg.prop('y2', '175'), + // svg.prop('stroke', progress == 100 ? colors.green : 'grey'), + // svg.prop('stroke-width', '6') + // ), + string.concat( + 'x1="230" y1="155" x2="230" y2="175" stroke="', + progress == 100 ? colors.green : "grey", + '" stroke-width="6"' + ), + NULL + ); + } + + uint256 len = (168 * progress) / 100; + string memory current = LibString.toString(62 + len); + + string memory progressLine = svg.line( + // string.concat( + // svg.prop('x1', '62'), + // svg.prop('y1', '165'), + // svg.prop('x2', current), + // svg.prop('y2', '165'), + // svg.prop('stroke', "url('#blueGreenGradient')"), + // svg.prop('stroke-width', '6') + // ), + string.concat( + 'x1="62" y1="165" x2="', + current, + '" y2="165" stroke="url(#blueGreenGradient)" stroke-width="6"' + ), + NULL + ); + + string memory progressCircle = svg.circle( + // string.concat( + // svg.prop('cx', current), + // svg.prop('cy', '165'), + // svg.prop('r', '6'), + // svg.prop('fill', "url('#blueGreenGradient')") + // ), + string.concat('cx="', current, '" cy="165" r="6" fill="url(#blueGreenGradient)"'), + NULL + ); + + string memory shadowLine = svg.line( + // string.concat( + // svg.prop('x1', '63'), + // svg.prop('y1', '165'), + // svg.prop('x2', '230'), + // svg.prop('y2', '165'), + // svg.prop('stroke', 'grey'), + // svg.prop('stroke-width', '4') + // ), + string.concat('x1="63" y1="165" x2="230" y2="165" stroke="grey" stroke-width="4"'), + NULL + ); + + return svg.g( + NULL, + string.concat( + startBar, + shadowLine, + progressLine, + progress < 15 ? "" : animateLine(len), + endBar, + progress < 5 || progress > 95 ? "" : progressCircle + ) + ); + } + + function animateLine(uint256 len) internal pure returns (string memory) { + return svg.rect( + // string.concat( + // svg.prop('x', '62'), + // svg.prop('y', '161'), + // svg.prop('width', '12'), + // svg.prop('height', '8'), + // svg.prop('fill', "url('#blueGreenGradient')"), + // svg.prop('rx', '4'), + // svg.prop('ry', '4') + // ), + string.concat( + 'x="62" y="161" width="12" height="8" fill="url(#blueGreenGradient)" rx="4" ry="4"' + ), + svg.el( + "animate", + // string.concat( + // svg.prop('attributeName', 'x'), + // svg.prop('values', string.concat('62;', LibString.toString(62 + len - 16), ';')), + // svg.prop('dur', string.concat(LibString.toString(((5 * len) / 168) + 1), 's')), + // svg.prop('repeatCount', 'indefinite') + // ), + string.concat( + 'attributeName="x" values="62;', + LibString.toString(62 + len - 16), + ';" dur="', + LibString.toString(((5 * len) / 168) + 1), + 's" repeatCount="indefinite"' + ), + NULL + ) + ); + } + + function progressLabels(uint48 start_, uint48 expiry_) internal pure returns (string memory) { + (string memory start, string memory expiry) = getTimeStrings(start_, expiry_); + + return string.concat( + svg.text( + // string.concat( + // svg.prop('x', '60'), + // svg.prop('y', '200'), + // svg.prop('font-size', '12'), + // TEXT_STYLE + // ), + string.concat('x="60" y="200" font-size="12" ', TEXT_STYLE), + start + ), + svg.text( + // string.concat( + // svg.prop('x', '230'), + // svg.prop('y', '200'), + // svg.prop('font-size', '12'), + // TEXT_STYLE + // ), + string.concat('x="230" y="200" font-size="12"', TEXT_STYLE), + expiry + ) + ); + } + + // ========== COLOR GRADIENTS ========== // + + function fullGradientStops() internal view returns (string memory) { + // return string.concat( + // svg.gradientStop( + // 2, + // colors.blue, + // NULL + // ), + // svg.gradientStop( + // 10, + // colors.lightBlue, + // NULL + // ), + // svg.gradientStop( + // 32, + // colors.green, + // NULL + // ), + // svg.gradientStop( + // 49, + // colors.yellowGreen, + // NULL + // ), + // svg.gradientStop( + // 52, + // colors.yellow, + // NULL + // ), + // svg.gradientStop( + // 79, + // colors.orange, + // NULL + // ), + // svg.gradientStop( + // 100, + // colors.red, + // NULL + // ) + // ); + return string.concat( + '', + '', + '', + '', + '', + '', + '' + ); + } + + function fullGradientReverseStops() internal view returns (string memory) { + return string.concat( + svg.gradientStop(2, colors.red, NULL), + svg.gradientStop(21, colors.orange, NULL), + svg.gradientStop(48, colors.yellow, NULL), + svg.gradientStop(51, colors.yellowGreen, NULL), + svg.gradientStop(68, colors.green, NULL), + svg.gradientStop(90, colors.lightBlue, NULL), + svg.gradientStop(100, colors.blue, NULL) + ); + return string.concat( + '', + '', + '', + '', + '', + '', + '' + ); + } + + function fullGradient() internal view returns (string memory) { + return + svg.linearGradient(string.concat(svg.prop("id", "fullGradient")), fullGradientStops()); + } + + function fullGradient90() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop("id", "fullGradient90"), svg.prop("gradientTransform", "rotate(90)") + ), + fullGradientStops() + ); + } + + function fullGradientReverse90() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop("id", "fullGradientReverse90"), svg.prop("gradientTransform", "rotate(90)") + ), + fullGradientReverseStops() + ); + } + + function blueGreenGradient() internal view returns (string memory) { + return svg.linearGradient( + string.concat( + svg.prop("id", "blueGreenGradient"), svg.prop("gradientUnits", "userSpaceOnUse") + ), + string.concat( + '', + '', + '' + ) + ); + } + + // function orangeRedGradient() internal view returns (string memory) { + // return svg.linearGradient( + // string.concat( + // svg.prop('id', 'orangeRedGradient'), + // svg.prop('gradientTransform', 'rotate(90)') + // ), + // string.concat( + // svg.gradientStop( + // 30, + // colors.orange, + // NULL + // ), + // svg.gradientStop( + // 90, + // colors.red, + // NULL + // ) + // ) + // ); + // } + + // ========== UTILITIES ========== // + + function getTimeStrings( + uint48 start, + uint48 end + ) internal pure returns (string memory, string memory) { + (string memory startYear, string memory startMonth, string memory startDay) = + Timestamp.toPaddedString(start); + (string memory endYear, string memory endMonth, string memory endDay) = + Timestamp.toPaddedString(end); + + return ( + string.concat(startYear, "-", startMonth, "-", startDay), + string.concat(endYear, "-", endMonth, "-", endDay) + ); + } + + // ========== EXAMPLE ========== // + // Used by hot-chain-svg during development. TODO: Remove for prod. + + function example() external view returns (string memory) { + Info memory info = Info({ + baseAssetSymbol: "XYZ", + tokenId: 123_456, + start: 1_717_200_000, // 2024-06-01 + expiry: 1_725_148_800 // 2024-09-01 + }); + + return render(info); + } +} diff --git a/test/modules/derivatives/mocks/MockDerivativeModule.sol b/test/modules/derivatives/mocks/MockDerivativeModule.sol index 5bc36d96..8f248112 100644 --- a/test/modules/derivatives/mocks/MockDerivativeModule.sol +++ b/test/modules/derivatives/mocks/MockDerivativeModule.sol @@ -209,4 +209,6 @@ contract MockDerivativeModule is DerivativeModule { function symbol(uint256 tokenId_) public view virtual override returns (string memory) {} function decimals(uint256 tokenId_) public view virtual override returns (uint8) {} + + function tokenURI(uint256 tokenId_) public view virtual override returns (string memory) {} } diff --git a/utils/hot-chain-svg/src/compile.js b/utils/hot-chain-svg/src/compile.js index 5077a8ad..93665495 100644 --- a/utils/hot-chain-svg/src/compile.js +++ b/utils/hot-chain-svg/src/compile.js @@ -12,10 +12,10 @@ function getSolcInput(source) { }, settings: { optimizer: { - enabled: false, - runs: 1, + enabled: true, + runs: 200, }, - evmVersion: 'london', + evmVersion: 'paris', outputSelection: { '*': { '*': ['abi', 'evm.bytecode'], diff --git a/utils/hot-chain-svg/src/index.js b/utils/hot-chain-svg/src/index.js index b069547c..bba813c7 100644 --- a/utils/hot-chain-svg/src/index.js +++ b/utils/hot-chain-svg/src/index.js @@ -6,7 +6,7 @@ const call = require('./call'); const compile = require('./compile'); const deploy = require('./deploy'); -const SOURCE = path.join(__dirname, '..', '..', '..', 'src', 'modules', 'derivatives', 'DerivativeCardDark.sol'); +const SOURCE = path.join(__dirname, '..', '..', '..', 'src', 'modules', 'derivatives', 'LinearVestingCard.sol'); const PROJECT_DIR = path.join(__dirname, '..', '..', '..'); async function main() { From 3fc2363c78580e73b885461e9c9c82d9c56aa0f1 Mon Sep 17 00:00:00 2001 From: Oighty Date: Tue, 25 Jun 2024 15:24:09 -0400 Subject: [PATCH 05/14] chore: linting and cleanup --- src/modules/derivatives/LinearVesting.sol | 7 +- src/modules/derivatives/LinearVestingCard.sol | 412 +++++------------- 2 files changed, 101 insertions(+), 318 deletions(-) diff --git a/src/modules/derivatives/LinearVesting.sol b/src/modules/derivatives/LinearVesting.sol index 9056b00e..95ecf244 100644 --- a/src/modules/derivatives/LinearVesting.sol +++ b/src/modules/derivatives/LinearVesting.sol @@ -42,8 +42,7 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { // ========== MODULE SETUP ========== // - constructor(address parent_) Module(parent_) { - // LinearVestingCard() + constructor(address parent_) Module(parent_) LinearVestingCard() { // Deploy the clone implementation _IMPLEMENTATION = address(new SoulboundCloneERC20()); } @@ -712,14 +711,16 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { }); // Return the token URI + // solhint-disable quotes return string.concat( '{"name": "', name(tokenId_), '", "description": "Linear Vesting ', _symbol, '", "image": "data:image/svg+xml;utf8,', - render(info), + _render(info), '"}' ); + // solhint-enable quotes } } diff --git a/src/modules/derivatives/LinearVestingCard.sol b/src/modules/derivatives/LinearVestingCard.sol index b2f10c2d..bc619e0f 100644 --- a/src/modules/derivatives/LinearVestingCard.sol +++ b/src/modules/derivatives/LinearVestingCard.sol @@ -7,6 +7,8 @@ import {Timestamp} from "src/lib/Timestamp.sol"; import {Strings as LibString} from "lib/openzeppelin-contracts/contracts/utils/Strings.sol"; contract LinearVestingCard { + // solhint-disable quotes + // ========== DATA STRUCTURES ========== // struct Info { @@ -16,182 +18,93 @@ contract LinearVestingCard { uint48 expiry; } - struct Colors { - string blue; - string lightBlue; - string green; - string yellowGreen; - string yellow; - string orange; - string red; - } - // ========== STATE VARIABLES ========== // - string internal constant TEXT_STYLE = + string internal constant _TEXT_STYLE = 'font-family="\'Menlo\', monospace" fill="white" text-anchor="middle"'; - string internal constant NULL = ""; - string internal ADDR_STRING; + string internal constant _NULL = ""; + string internal _addrString; - Colors internal colors; + string internal constant _COLOR_BLUE = "rgb(110, 148, 240)"; + string internal constant _COLOR_LIGHT_BLUE = "rgb(118, 189, 242)"; + string internal constant _COLOR_GREEN = "rgb(206, 244, 117)"; + string internal constant _COLOR_YELLOW_GREEN = "rgb(243, 244, 189)"; + string internal constant _COLOR_YELLOW = "rgb(243, 244, 189)"; + string internal constant _COLOR_ORANGE = "rgb(246, 172, 84)"; + string internal constant _COLOR_RED = "rgb(242, 103, 64)"; // ========== CONSTRUCTOR ========== // constructor() { - colors = Colors({ - blue: "rgb(110, 148, 240)", - lightBlue: "rgb(118, 189, 242)", - green: "rgb(206, 244, 117)", - yellowGreen: "rgb(243, 244, 189)", - yellow: "rgb(243, 244, 189)", - orange: "rgb(246, 172, 84)", - red: "rgb(242, 103, 64)" - }); - - ADDR_STRING = LibString.toHexString(address(this)); + _addrString = LibString.toHexString(address(this)); } // ========== RENDERER ========== // - function render(Info memory tokenInfo) internal view returns (string memory) { + function _render(Info memory tokenInfo) internal view returns (string memory) { return string.concat( '', svg.el( "defs", - NULL, + _NULL, string.concat( - fullGradient(), fullGradient90(), fullGradientReverse90(), blueGreenGradient() + _fullGradient(), + _fullGradient90(), + _fullGradientReverse90(), + _blueGreenGradient() ) ), svg.rect( - // string.concat( - // svg.prop('x', '0'), - // svg.prop('y', '0'), - // svg.prop('width', '290'), - // svg.prop('height', '500'), - // svg.prop('fill', 'rgb(30, 30, 30)'), - // svg.prop('rx', '25'), - // svg.prop('ry', '25') - // ), - 'x="0" y="0" width="290" height="500" fill="rgb(30, 30, 30)" rx="25" ry="25"', - NULL + 'x="0" y="0" width="290" height="500" fill="rgb(30, 30, 30)" rx="25" ry="25"', _NULL ), svg.rect( - // string.concat( - // svg.prop('x', '8'), - // svg.prop('y', '8'), - // svg.prop('width', '274'), - // svg.prop('height', '484'), - // svg.prop('fill', 'none'), - // svg.prop('stroke', "url('#fullGradient')"), - // svg.prop('stroke-width', '2'), - // svg.prop('rx', '20'), - // svg.prop('ry', '20') - // ), 'x="8" y="8" width="274" height="484" fill="none" stroke="url(#fullGradient)" stroke-width="2" rx="20" ry="20"', - NULL + _NULL ), - title(tokenInfo.baseAssetSymbol), - progressBar(uint256(tokenInfo.start), uint256(tokenInfo.expiry)), - progressLabels(tokenInfo.start, tokenInfo.expiry), - logo(), - identifier(tokenInfo.tokenId), + _title(tokenInfo.baseAssetSymbol), + _progressBar(uint256(tokenInfo.start), uint256(tokenInfo.expiry)), + _progressLabels(tokenInfo.start, tokenInfo.expiry), + _logo(), + _identifier(tokenInfo.tokenId), "" ); } // ========== COMPONENTS ========== // - function title(string memory symbol) internal pure returns (string memory) { + function _title(string memory symbol) internal pure returns (string memory) { return string.concat( - svg.text( - // string.concat( - // svg.prop('x', '145'), - // svg.prop('y', '40'), - // svg.prop('font-size', '20'), - // TEXT_STYLE - // ), - string.concat('x="145" y="40" font-size="20" ', TEXT_STYLE), - "Linear Vesting" - ), - svg.text( - // string.concat( - // svg.prop('x', '145'), - // svg.prop('y', '100'), - // svg.prop('font-size', '56'), - // TEXT_STYLE - // ), - string.concat('x="145" y="100" font-size="56" ', TEXT_STYLE), - symbol - ) + svg.text(string.concat('x="145" y="40" font-size="20" ', _TEXT_STYLE), "Linear Vesting"), + svg.text(string.concat('x="145" y="100" font-size="56" ', _TEXT_STYLE), symbol) ); } - function logo() internal pure returns (string memory) { + function _logo() internal pure returns (string memory) { return string.concat( svg.rect( - // string.concat( - // svg.prop('x', '143'), - // svg.prop('y', '240'), - // svg.prop('width', '6'), - // svg.prop('height', '125'), - // svg.prop('fill', "url('#fullGradientReverse90')") - // ), - 'x="143" y="240" width="6" height="125" fill="url(#fullGradientReverse90)"', - NULL + 'x="143" y="240" width="6" height="125" fill="url(#fullGradientReverse90)"', _NULL ), svg.rect( - // string.concat( - // svg.prop('x', '79'), - // svg.prop('y', '246'), - // svg.prop('width', '6'), - // svg.prop('height', '125'), - // svg.prop('fill', "url('#fullGradient90')"), - // svg.prop('transform', 'rotate(-60 145 250)') - // ), 'x="79" y="246" width="6" height="125" fill="url(#fullGradient90)" transform="rotate(-60 145 250)"', - NULL + _NULL ), svg.rect( - // string.concat( - // svg.prop('x', '206'), - // svg.prop('y', '244'), - // svg.prop('width', '6'), - // svg.prop('height', '125'), - // svg.prop('fill', "url('#fullGradient90')"), - // svg.prop('transform', 'rotate(60 145 250)') - // ), 'x="206" y="244" width="6" height="125" fill="url(#fullGradient90)" transform="rotate(60 145 250)"', - NULL + _NULL ) ); } - function identifier(uint256 tokenId) internal view returns (string memory) { + function _identifier(uint256 tokenId) internal view returns (string memory) { return string.concat( + svg.text(string.concat('x="145" y="460" font-size="10" ', _TEXT_STYLE), _addrString), svg.text( - // string.concat( - // svg.prop('x', '145'), - // svg.prop('y', '460'), - // svg.prop('font-size', '10'), - // TEXT_STYLE - // ), - string.concat('x="145" y="460" font-size="10" ', TEXT_STYLE), - ADDR_STRING - ), - svg.text( - // string.concat( - // svg.prop('x', '145'), - // svg.prop('y', '480'), - // svg.prop('font-size', '10'), - // TEXT_STYLE - // ), - string.concat('x="145" y="480" font-size="10" ', TEXT_STYLE), + string.concat('x="145" y="480" font-size="10" ', _TEXT_STYLE), string.concat("ID: ", LibString.toString(tokenId)) ) ); } - function progressBar(uint256 start, uint256 end) internal view returns (string memory) { + function _progressBar(uint256 start, uint256 end) internal view returns (string memory) { uint256 currentTime = block.timestamp; // 1717200000 + 70 * 86400; string memory startBar; @@ -205,37 +118,21 @@ contract LinearVestingCard { progress = progress > 100 ? 100 : progress; startBar = svg.line( - // string.concat( - // svg.prop('x1', '60'), - // svg.prop('y1', '155'), - // svg.prop('x2', '60'), - // svg.prop('y2', '175'), - // svg.prop('stroke', started ? colors.blue : 'grey'), - // svg.prop('stroke-width', '6') - // ), string.concat( 'x1="60" y1="155" x2="60" y2="175" stroke="', - started ? colors.blue : "grey", + started ? _COLOR_BLUE : "grey", '" stroke-width="6"' ), - NULL + _NULL ); endBar = svg.line( - // string.concat( - // svg.prop('x1', '230'), - // svg.prop('y1', '155'), - // svg.prop('x2', '230'), - // svg.prop('y2', '175'), - // svg.prop('stroke', progress == 100 ? colors.green : 'grey'), - // svg.prop('stroke-width', '6') - // ), string.concat( 'x1="230" y1="155" x2="230" y2="175" stroke="', - progress == 100 ? colors.green : "grey", + progress == 100 ? _COLOR_GREEN : "grey", '" stroke-width="6"' ), - NULL + _NULL ); } @@ -243,81 +140,43 @@ contract LinearVestingCard { string memory current = LibString.toString(62 + len); string memory progressLine = svg.line( - // string.concat( - // svg.prop('x1', '62'), - // svg.prop('y1', '165'), - // svg.prop('x2', current), - // svg.prop('y2', '165'), - // svg.prop('stroke', "url('#blueGreenGradient')"), - // svg.prop('stroke-width', '6') - // ), string.concat( 'x1="62" y1="165" x2="', current, '" y2="165" stroke="url(#blueGreenGradient)" stroke-width="6"' ), - NULL + _NULL ); string memory progressCircle = svg.circle( - // string.concat( - // svg.prop('cx', current), - // svg.prop('cy', '165'), - // svg.prop('r', '6'), - // svg.prop('fill', "url('#blueGreenGradient')") - // ), - string.concat('cx="', current, '" cy="165" r="6" fill="url(#blueGreenGradient)"'), - NULL + string.concat('cx="', current, '" cy="165" r="6" fill="url(#blueGreenGradient)"'), _NULL ); string memory shadowLine = svg.line( - // string.concat( - // svg.prop('x1', '63'), - // svg.prop('y1', '165'), - // svg.prop('x2', '230'), - // svg.prop('y2', '165'), - // svg.prop('stroke', 'grey'), - // svg.prop('stroke-width', '4') - // ), string.concat('x1="63" y1="165" x2="230" y2="165" stroke="grey" stroke-width="4"'), - NULL + _NULL ); return svg.g( - NULL, + _NULL, string.concat( startBar, shadowLine, progressLine, - progress < 15 ? "" : animateLine(len), + progress < 15 ? "" : _animateLine(len), endBar, progress < 5 || progress > 95 ? "" : progressCircle ) ); } - function animateLine(uint256 len) internal pure returns (string memory) { + function _animateLine(uint256 len) internal pure returns (string memory) { return svg.rect( - // string.concat( - // svg.prop('x', '62'), - // svg.prop('y', '161'), - // svg.prop('width', '12'), - // svg.prop('height', '8'), - // svg.prop('fill', "url('#blueGreenGradient')"), - // svg.prop('rx', '4'), - // svg.prop('ry', '4') - // ), string.concat( 'x="62" y="161" width="12" height="8" fill="url(#blueGreenGradient)" rx="4" ry="4"' ), svg.el( "animate", - // string.concat( - // svg.prop('attributeName', 'x'), - // svg.prop('values', string.concat('62;', LibString.toString(62 + len - 16), ';')), - // svg.prop('dur', string.concat(LibString.toString(((5 * len) / 168) + 1), 's')), - // svg.prop('repeatCount', 'indefinite') - // ), string.concat( 'attributeName="x" values="62;', LibString.toString(62 + len - 16), @@ -325,204 +184,128 @@ contract LinearVestingCard { LibString.toString(((5 * len) / 168) + 1), 's" repeatCount="indefinite"' ), - NULL + _NULL ) ); } - function progressLabels(uint48 start_, uint48 expiry_) internal pure returns (string memory) { - (string memory start, string memory expiry) = getTimeStrings(start_, expiry_); + function _progressLabels(uint48 start_, uint48 expiry_) internal pure returns (string memory) { + (string memory start, string memory expiry) = _getTimeStrings(start_, expiry_); return string.concat( - svg.text( - // string.concat( - // svg.prop('x', '60'), - // svg.prop('y', '200'), - // svg.prop('font-size', '12'), - // TEXT_STYLE - // ), - string.concat('x="60" y="200" font-size="12" ', TEXT_STYLE), - start - ), - svg.text( - // string.concat( - // svg.prop('x', '230'), - // svg.prop('y', '200'), - // svg.prop('font-size', '12'), - // TEXT_STYLE - // ), - string.concat('x="230" y="200" font-size="12"', TEXT_STYLE), - expiry - ) + svg.text(string.concat('x="60" y="200" font-size="12" ', _TEXT_STYLE), start), + svg.text(string.concat('x="230" y="200" font-size="12"', _TEXT_STYLE), expiry) ); } // ========== COLOR GRADIENTS ========== // - function fullGradientStops() internal view returns (string memory) { - // return string.concat( - // svg.gradientStop( - // 2, - // colors.blue, - // NULL - // ), - // svg.gradientStop( - // 10, - // colors.lightBlue, - // NULL - // ), - // svg.gradientStop( - // 32, - // colors.green, - // NULL - // ), - // svg.gradientStop( - // 49, - // colors.yellowGreen, - // NULL - // ), - // svg.gradientStop( - // 52, - // colors.yellow, - // NULL - // ), - // svg.gradientStop( - // 79, - // colors.orange, - // NULL - // ), - // svg.gradientStop( - // 100, - // colors.red, - // NULL - // ) - // ); + function _fullGradientStops() internal view returns (string memory) { return string.concat( '', '', '', '', '', '', '' ); } - function fullGradientReverseStops() internal view returns (string memory) { + function _fullGradientReverseStops() internal view returns (string memory) { return string.concat( - svg.gradientStop(2, colors.red, NULL), - svg.gradientStop(21, colors.orange, NULL), - svg.gradientStop(48, colors.yellow, NULL), - svg.gradientStop(51, colors.yellowGreen, NULL), - svg.gradientStop(68, colors.green, NULL), - svg.gradientStop(90, colors.lightBlue, NULL), - svg.gradientStop(100, colors.blue, NULL) + svg.gradientStop(2, _COLOR_RED, _NULL), + svg.gradientStop(21, _COLOR_ORANGE, _NULL), + svg.gradientStop(48, _COLOR_YELLOW, _NULL), + svg.gradientStop(51, _COLOR_YELLOW_GREEN, _NULL), + svg.gradientStop(68, _COLOR_GREEN, _NULL), + svg.gradientStop(90, _COLOR_LIGHT_BLUE, _NULL), + svg.gradientStop(100, _COLOR_BLUE, _NULL) ); return string.concat( '', '', '', '', '', '', '' ); } - function fullGradient() internal view returns (string memory) { + function _fullGradient() internal view returns (string memory) { return - svg.linearGradient(string.concat(svg.prop("id", "fullGradient")), fullGradientStops()); + svg.linearGradient(string.concat(svg.prop("id", "fullGradient")), _fullGradientStops()); } - function fullGradient90() internal view returns (string memory) { + function _fullGradient90() internal view returns (string memory) { return svg.linearGradient( string.concat( svg.prop("id", "fullGradient90"), svg.prop("gradientTransform", "rotate(90)") ), - fullGradientStops() + _fullGradientStops() ); } - function fullGradientReverse90() internal view returns (string memory) { + function _fullGradientReverse90() internal view returns (string memory) { return svg.linearGradient( string.concat( svg.prop("id", "fullGradientReverse90"), svg.prop("gradientTransform", "rotate(90)") ), - fullGradientReverseStops() + _fullGradientReverseStops() ); } - function blueGreenGradient() internal view returns (string memory) { + function _blueGreenGradient() internal view returns (string memory) { return svg.linearGradient( string.concat( svg.prop("id", "blueGreenGradient"), svg.prop("gradientUnits", "userSpaceOnUse") ), string.concat( '', '', '' ) ); } - // function orangeRedGradient() internal view returns (string memory) { - // return svg.linearGradient( - // string.concat( - // svg.prop('id', 'orangeRedGradient'), - // svg.prop('gradientTransform', 'rotate(90)') - // ), - // string.concat( - // svg.gradientStop( - // 30, - // colors.orange, - // NULL - // ), - // svg.gradientStop( - // 90, - // colors.red, - // NULL - // ) - // ) - // ); - // } - // ========== UTILITIES ========== // - function getTimeStrings( + function _getTimeStrings( uint48 start, uint48 end ) internal pure returns (string memory, string memory) { @@ -538,16 +321,15 @@ contract LinearVestingCard { } // ========== EXAMPLE ========== // - // Used by hot-chain-svg during development. TODO: Remove for prod. - - function example() external view returns (string memory) { - Info memory info = Info({ - baseAssetSymbol: "XYZ", - tokenId: 123_456, - start: 1_717_200_000, // 2024-06-01 - expiry: 1_725_148_800 // 2024-09-01 - }); - - return render(info); - } + // Used by hot-chain-svg during development. + // + // function example() external view returns (string memory) { + // Info memory info = Info({ + // baseAssetSymbol: "XYZ", + // tokenId: 123_456, + // start: 1_717_200_000, // 2024-06-01 + // expiry: 1_725_148_800 // 2024-09-01 + // }); + // return _render(info); + // } } From eaae37ea8c73a196d92d1cf2870b8761009e138f Mon Sep 17 00:00:00 2001 From: Oighty Date: Tue, 2 Jul 2024 11:37:20 -0400 Subject: [PATCH 06/14] feat: add totalSupply value to ERC6909 derivatives --- src/interfaces/modules/IDerivative.sol | 15 ++++++-- src/lib/ERC6909Metadata.sol | 6 ++++ src/modules/derivatives/LinearVesting.sol | 35 ++++++++++++++++--- .../mocks/MockDerivativeModule.sol | 2 ++ 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/interfaces/modules/IDerivative.sol b/src/interfaces/modules/IDerivative.sol index a4ea48b7..c9a1c487 100644 --- a/src/interfaces/modules/IDerivative.sol +++ b/src/interfaces/modules/IDerivative.sol @@ -14,13 +14,15 @@ interface IDerivative { /// @notice Metadata for a derivative token /// /// @param exists True if the token has been deployed - /// @param wrapped True if an ERC20-wrapped derivative has been deployed + /// @param wrapped Non-zero if an ERC20-wrapped derivative has been deployed /// @param underlyingToken The address of the underlying token + /// @param supply The total supply of the derivative token /// @param data Implementation-specific data struct Token { bool exists; address wrapped; address underlyingToken; + uint256 supply; bytes data; } @@ -30,13 +32,20 @@ interface IDerivative { /// /// @param tokenId The ID of the derivative token /// @return exists True if the token has been deployed - /// @return wrapped True if an ERC20-wrapped derivative has been deployed + /// @return wrapped Non-zero if an ERC20-wrapped derivative has been deployed /// @return underlyingToken The address of the underlying token + /// @return supply The total supply of the derivative token /// @return data Implementation-specific data function tokenMetadata(uint256 tokenId) external view - returns (bool exists, address wrapped, address underlyingToken, bytes memory data); + returns ( + bool exists, + address wrapped, + address underlyingToken, + uint256 supply, + bytes memory data + ); // ========== DERIVATIVE MANAGEMENT ========== // diff --git a/src/lib/ERC6909Metadata.sol b/src/lib/ERC6909Metadata.sol index 5229e49e..3d83cd01 100644 --- a/src/lib/ERC6909Metadata.sol +++ b/src/lib/ERC6909Metadata.sol @@ -25,4 +25,10 @@ abstract contract ERC6909Metadata { /// @param tokenId_ The ID of the token /// @return string The URI of the token function tokenURI(uint256 tokenId_) public view virtual returns (string memory); + + /// @notice Returns the total supply of the token + /// + /// @param tokenId_ The ID of the token + /// @return uint256 The total supply of the token + function totalSupply(uint256 tokenId_) public view virtual returns (uint256); } diff --git a/src/modules/derivatives/LinearVesting.sol b/src/modules/derivatives/LinearVesting.sol index 95ecf244..065fd843 100644 --- a/src/modules/derivatives/LinearVesting.sol +++ b/src/modules/derivatives/LinearVesting.sol @@ -145,6 +145,11 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { _deployWrapIfNeeded(tokenId_, token_); } + // Increment the supply + unchecked { + token_.supply += amount_; + } + // Transfer collateral token to this contract { ERC20 baseToken = ERC20(token_.underlyingToken); @@ -266,10 +271,16 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { // Burn the unwrapped tokens if (derivativeToBurn > 0) { + unchecked { + tokenData.supply -= derivativeToBurn; + } _burn(user_, tokenId_, derivativeToBurn); } // Burn the wrapped tokens - will be 0 if not wrapped if (wrappedToBurn > 0) { + unchecked { + tokenData.supply -= wrappedToBurn; + } SoulboundCloneERC20(tokenData.wrapped).burn(user_, wrappedToBurn); } @@ -395,6 +406,8 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { if (balanceOf[msg.sender][tokenId_] < amount_) revert InsufficientBalance(); + // Supply does not change + // Burn the derivative token _burn(msg.sender, tokenId_, amount_); @@ -424,6 +437,8 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { if (wrappedToken.balanceOf(msg.sender) < amount_) revert InsufficientBalance(); + // Supply does not change + // Burn the wrapped derivative token wrappedToken.burn(msg.sender, amount_); @@ -705,22 +720,34 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { // Create token info for rendering token card Info memory info = Info({ tokenId: tokenId_, - baseAssetSymbol: _symbol, + baseToken: token.underlyingToken, + baseTokenSymbol: _symbol, start: data.start, - expiry: data.expiry + expiry: data.expiry, + supply: totalSupply(tokenId_) }); // Return the token URI // solhint-disable quotes return string.concat( + "data:application/json;utf8,", '{"name": "', name(tokenId_), - '", "description": "Linear Vesting ', + '", "description": "', _symbol, - '", "image": "data:image/svg+xml;utf8,', + ' Soulbound Linear Vesting Derivative Token. Powered by Axis.", "attributes": ', + _attributes(info), + ', "image": "data:image/svg+xml;utf8,', _render(info), '"}' ); // solhint-enable quotes } + + // ========== ERC6909 TOKEN SUPPLY EXTENSION ========== // + + /// @inheritdoc ERC6909Metadata + function totalSupply(uint256 tokenId_) public view override returns (uint256) { + return tokenMetadata[tokenId_].supply; + } } diff --git a/test/modules/derivatives/mocks/MockDerivativeModule.sol b/test/modules/derivatives/mocks/MockDerivativeModule.sol index 8f248112..96610554 100644 --- a/test/modules/derivatives/mocks/MockDerivativeModule.sol +++ b/test/modules/derivatives/mocks/MockDerivativeModule.sol @@ -211,4 +211,6 @@ contract MockDerivativeModule is DerivativeModule { function decimals(uint256 tokenId_) public view virtual override returns (uint8) {} function tokenURI(uint256 tokenId_) public view virtual override returns (string memory) {} + + function totalSupply(uint256 tokenId_) public view virtual override returns (uint256) {} } From a5d9f3ce5032d859be1b32b31d32b588ea8d5262 Mon Sep 17 00:00:00 2001 From: Oighty Date: Tue, 2 Jul 2024 11:37:46 -0400 Subject: [PATCH 07/14] feat: update Linear Vesting tokenURI and cleanup SVG card --- src/modules/derivatives/LinearVestingCard.sol | 122 ++++++++---------- 1 file changed, 53 insertions(+), 69 deletions(-) diff --git a/src/modules/derivatives/LinearVestingCard.sol b/src/modules/derivatives/LinearVestingCard.sol index bc619e0f..8c4ceab9 100644 --- a/src/modules/derivatives/LinearVestingCard.sol +++ b/src/modules/derivatives/LinearVestingCard.sol @@ -3,8 +3,7 @@ pragma solidity 0.8.19; import {svg} from "src/lib/SVG.sol"; import {Timestamp} from "src/lib/Timestamp.sol"; -// import {LibString} from "lib/solady/src/utils/LibString.sol"; -import {Strings as LibString} from "lib/openzeppelin-contracts/contracts/utils/Strings.sol"; +import {Strings} from "lib/openzeppelin-contracts/contracts/utils/Strings.sol"; contract LinearVestingCard { // solhint-disable quotes @@ -13,9 +12,11 @@ contract LinearVestingCard { struct Info { uint256 tokenId; - string baseAssetSymbol; + address baseToken; + string baseTokenSymbol; uint48 start; uint48 expiry; + uint256 supply; } // ========== STATE VARIABLES ========== // @@ -36,7 +37,32 @@ contract LinearVestingCard { // ========== CONSTRUCTOR ========== // constructor() { - _addrString = LibString.toHexString(address(this)); + _addrString = Strings.toHexString(address(this)); + } + + // ========== ATTRIBUTES ========== // + + function _attributes(Info memory tokenInfo) internal view returns (string memory) { + return string.concat( + '[{"trait_type":"Token ID","value":"', + Strings.toString(tokenInfo.tokenId), + '"},', + '{"trait_type":"Base Token","value":"', + Strings.toHexString(tokenInfo.baseToken), + '"},', + '{"trait_type":"Base Token Symbol", "value":"', + tokenInfo.baseTokenSymbol, + '"},', + '{"trait_type":"Vesting Start","display_type":"date","value":"', + Strings.toString(tokenInfo.start), + '"},', + '{"trait_type":"Vesting End","display_type":"date","value":"', + Strings.toString(tokenInfo.expiry), + '"},', + '{"trait_type":"Total Supply","value":"', + Strings.toString(tokenInfo.supply), + '"}]' + ); } // ========== RENDERER ========== // @@ -60,7 +86,7 @@ contract LinearVestingCard { 'x="8" y="8" width="274" height="484" fill="none" stroke="url(#fullGradient)" stroke-width="2" rx="20" ry="20"', _NULL ), - _title(tokenInfo.baseAssetSymbol), + _title(tokenInfo.baseTokenSymbol), _progressBar(uint256(tokenInfo.start), uint256(tokenInfo.expiry)), _progressLabels(tokenInfo.start, tokenInfo.expiry), _logo(), @@ -99,7 +125,7 @@ contract LinearVestingCard { svg.text(string.concat('x="145" y="460" font-size="10" ', _TEXT_STYLE), _addrString), svg.text( string.concat('x="145" y="480" font-size="10" ', _TEXT_STYLE), - string.concat("ID: ", LibString.toString(tokenId)) + string.concat("ID: ", Strings.toString(tokenId)) ) ); } @@ -137,7 +163,7 @@ contract LinearVestingCard { } uint256 len = (168 * progress) / 100; - string memory current = LibString.toString(62 + len); + string memory current = Strings.toString(62 + len); string memory progressLine = svg.line( string.concat( @@ -179,9 +205,9 @@ contract LinearVestingCard { "animate", string.concat( 'attributeName="x" values="62;', - LibString.toString(62 + len - 16), + Strings.toString(62 + len - 16), ';" dur="', - LibString.toString(((5 * len) / 168) + 1), + Strings.toString(((5 * len) / 168) + 1), 's" repeatCount="indefinite"' ), _NULL @@ -200,33 +226,19 @@ contract LinearVestingCard { // ========== COLOR GRADIENTS ========== // - function _fullGradientStops() internal view returns (string memory) { + function _fullGradientStops() internal pure returns (string memory) { return string.concat( - '', - '', - '', - '', - '', - '', - '' + svg.gradientStop(2, _COLOR_BLUE, _NULL), + svg.gradientStop(10, _COLOR_LIGHT_BLUE, _NULL), + svg.gradientStop(32, _COLOR_GREEN, _NULL), + svg.gradientStop(49, _COLOR_YELLOW_GREEN, _NULL), + svg.gradientStop(52, _COLOR_YELLOW, _NULL), + svg.gradientStop(79, _COLOR_ORANGE, _NULL), + svg.gradientStop(100, _COLOR_RED, _NULL) ); } - function _fullGradientReverseStops() internal view returns (string memory) { + function _fullGradientReverseStops() internal pure returns (string memory) { return string.concat( svg.gradientStop(2, _COLOR_RED, _NULL), svg.gradientStop(21, _COLOR_ORANGE, _NULL), @@ -236,37 +248,14 @@ contract LinearVestingCard { svg.gradientStop(90, _COLOR_LIGHT_BLUE, _NULL), svg.gradientStop(100, _COLOR_BLUE, _NULL) ); - return string.concat( - '', - '', - '', - '', - '', - '', - '' - ); } - function _fullGradient() internal view returns (string memory) { + function _fullGradient() internal pure returns (string memory) { return svg.linearGradient(string.concat(svg.prop("id", "fullGradient")), _fullGradientStops()); } - function _fullGradient90() internal view returns (string memory) { + function _fullGradient90() internal pure returns (string memory) { return svg.linearGradient( string.concat( svg.prop("id", "fullGradient90"), svg.prop("gradientTransform", "rotate(90)") @@ -275,7 +264,7 @@ contract LinearVestingCard { ); } - function _fullGradientReverse90() internal view returns (string memory) { + function _fullGradientReverse90() internal pure returns (string memory) { return svg.linearGradient( string.concat( svg.prop("id", "fullGradientReverse90"), svg.prop("gradientTransform", "rotate(90)") @@ -284,21 +273,15 @@ contract LinearVestingCard { ); } - function _blueGreenGradient() internal view returns (string memory) { + function _blueGreenGradient() internal pure returns (string memory) { return svg.linearGradient( string.concat( svg.prop("id", "blueGreenGradient"), svg.prop("gradientUnits", "userSpaceOnUse") ), string.concat( - '', - '', - '' + svg.gradientStop(20, _COLOR_BLUE, _NULL), + svg.gradientStop(40, _COLOR_LIGHT_BLUE, _NULL), + svg.gradientStop(60, _COLOR_GREEN, _NULL) ) ); } @@ -325,7 +308,8 @@ contract LinearVestingCard { // // function example() external view returns (string memory) { // Info memory info = Info({ - // baseAssetSymbol: "XYZ", + // baseToken: address(0x1234567890123456789012345678901234567890), + // baseTokenSymbol: "XYZ", // tokenId: 123_456, // start: 1_717_200_000, // 2024-06-01 // expiry: 1_725_148_800 // 2024-09-01 From 5530eea796c809ac995d3dd899c48114620d5719 Mon Sep 17 00:00:00 2001 From: Oighty Date: Tue, 2 Jul 2024 12:48:47 -0400 Subject: [PATCH 08/14] test: update salts --- script/salts/salts.json | 48 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/script/salts/salts.json b/script/salts/salts.json index 9bda0fb8..aad592b5 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -46,38 +46,38 @@ "0xed4ba02077e70f78db70ce191509333d2dcccfff136d30dcb011f567305e88ad": "0x2b8bc588174d409692f1bd993942e3806c9a11344dbe674f46aed32a030aacf1" }, "Test_AllocatedMerkleAllowlist": { - "0x1242d528fb3d9259e7192f83e1faf95370bfd8d7f2b1bae5d3ea350d3d9c563c": "0x0ec2c9d910490e897c952a3210e5915006080f59a14a022b941a8c8cef2886e3", - "0x7cca57da19a5847b2b87641c1b721a32d4778e3e58641d6771cd14ed112b3bc3": "0xe0631d27a3d241774729d8c3ee1d9efae407f1073f89fa0956b2d308bd3adec4" + "0x0a2d065ef3d2d6c130ea0c8f842eeea7beb95ef48b1920ab9be64d206b8757a8": "0x50f8e9eef931694bd5a50e9a0dbc557c356065ab9dfc8558286cf2b063a4e1e8", + "0x32b5e4fb41e10ecda1ccda8d203d8f92bbcb65cad3f7ea8b93e837db2f835054": "0x92175bc2333113ae45429d8caec2316254d0e2b5b7889fb503211515d0302e42" }, "Test_CappedMerkleAllowlist": { - "0x240bdd876f7c0070009c234b8cde981c3865d49db15097f59cd4ffb15d818d1e": "0xe68b5bbeed8a53eb7e97b7bc84ec08e1e6a5b360cae16f0653c60eff312dec3f", - "0xb8a17b3ee693ebdcfc6913bb2f65d0fadbe8812d9ef4d927595a7dc75db68727": "0x956b71c3545015d12e8bbefbad72019b673860f32a5b4313eeb866425d97be17" + "0x02736cfd6208962bd7a9eb511d4f48d434361d9eb5bc6df1207efa739621c0a2": "0xf3f0551fb48dad53afcfd6afb2298b8d7f25a9c6462139731d3f7e4ee1b8987f", + "0x55286ad58fe67b1a4910bf2553ef8ecf5fc7322b56369bad4421ab15e9bd34f3": "0x9f5e5aa6dc0e500eca4093ca7163a82e7e4e388ad60818531041389c801e186b" }, "Test_MockCallback": { - "0x0825d99059fe0dc95e0b5cd7b853a5f3f50b58f25c5812fc7fd01d69ff4ec679": "0x222b47fd0259b3eacfbc04b9dddf5bb5ba060245c2c3c3673672ad4d815d01df", - "0x51526251ca3003df174c65f2fb581f61820ce57c14720e0d67480ed8c8b77925": "0xd25a3174b866cae4f78f513123ffebf33114f3df0093e06159010b3d9fbadff3", - "0x6206c9845f35f3006fc507c15aaaf4a719380265cf472773a270ffe6d5eb5299": "0xe9943f7c46c50e34e5f91da14d0ca6c58b5504172e156e19108bf2844fdaec63", - "0x6b7fa77ae82f95cc8c5fe53dca64702ea0dc8e3dad74749dc8cd77de2884e704": "0x600196a40f70c0b91ab3ace5873750bc77d14dddf6b1093b03def43f31ddb4c3", - "0x7310243dabd76344326f3659aabda0b846a9c8e9e7d50b990261c14f2b44d560": "0x86d15e2125c36574017c86d4fcd04bb470225e835b76e3cbe63c5908da46ab8c", - "0x7602309c05771de8281f2f3f08e74ec9f19f1bfdaac8cd970d9f7a8d62173b6e": "0xd28f39fd2d2e1a378b0fe177abc5ac22a4642634847865a096a5e96bd04c6411", - "0x892e1120a976151ae5375dca978040abe705c8cbdd3c7b64b41ff1087ee01c22": "0x748af80730f660a2400a4d6f8f4f41d21cca1cc117fc2f1c51e131e16fa43ce3", - "0x8a69dcb863773a3bb971a969233286e75cba80abf6ec8d881cada7ea0ad674b0": "0xd7bacad37fd5e507c6bc424ed88ae37e8d4935b54d7e41cf1306d46c37eaebb4", - "0x8d8681a539f3b359e94a05239187dc55cb9cb2dcfe01726d3053573206efe25f": "0x6f209918e9619b620b306b2014e6aabe3688c4a4043117b6835ab17bd70bf004", - "0x918d87a20c837fa93ce3089d2cb6386b66f79bbfd0f09bd6784bc52108b69938": "0x64473a26214c7b928ff6c08104e3b18ae36918097eefb554aa17c2b9347fd7f2", - "0x9569f58e5e127ceb73049eea8119bb21f8c4328d3b4770da5a67075be3f77c12": "0x2123580432d5ece19c5e099d5156a9598f2c49fdcbcc1029f7d21276ccab2ba3", - "0x96f0481f00eeec717fa8c0d41efd04b6e3fdccbebbaa223ee2118449ebea2406": "0x6bc41d5bab2740be84e43b053ed3b46acb6f3ac1cb9031fb3203edd92a833a07", - "0x9c35a3189126603f4b187149f54e09082403dac10b3e899a503e28e897edd6f9": "0xa35f7a6870564c7a14d71310c87a6816c3ef4f80eb0a5ac43d56235e075ae162", - "0x9db4bc605469ede684fbaa475d320b3fbe6a707deaa953ee1ba29ecb3c1125fb": "0x67f66566dfb091aeeb776b692eba5f570f5a310cf25e94f0e06120cf64737a9d", - "0xb8e0b3cbd7273e83cfdbcb8269e5c3510a28892e815fe28bc0f515be0df2c8f9": "0x17f7fbab547a6a69119fee8820b0ff11a8da7a1f9902a19d8f3917a6c8aff2da", - "0xc38bb4813ebdb9978d2485ae5ad92e1152f8a955430a63210b69b2277eed9477": "0x9bf9b60e2afb2faf561385c61d5c3f5b6b910c97bdd3bb4af59d193140215a31", - "0xd75e639b43b1d29e1703abcd2616b6b67f3ae232d53915d472f3a5b6aae6083c": "0xa45904df2431729c77de44b4b88e032130e12dcbc735f0f72a29af686e15f3ed", - "0xf2644573f57aea10dffac6e7215b409fa19236ae57b735b20b9fac9834391850": "0x3a824c912eb7f08b82a8d936c2e29cb2b6fdde7e33ea9af18d9ccbde9c454161" + "0x0825d99059fe0dc95e0b5cd7b853a5f3f50b58f25c5812fc7fd01d69ff4ec679": "0x7115985c85966c2ae92532a236ed7a60433d064530db48acfaaffefac7d2ba80", + "0x51526251ca3003df174c65f2fb581f61820ce57c14720e0d67480ed8c8b77925": "0xc069eb6d2e6e633369b7ae57a983bfab95859aec80f035423bcc0d37adc6ba6a", + "0x6206c9845f35f3006fc507c15aaaf4a719380265cf472773a270ffe6d5eb5299": "0x9a9d1e9fed3ed0572252fde51fcbc1fd2f97c2dbfc929d18d19d11d10306592a", + "0x6b7fa77ae82f95cc8c5fe53dca64702ea0dc8e3dad74749dc8cd77de2884e704": "0x397784e4562938597f74b1bf92a4ce0bfe5e7d592831800acb128eff47c5f541", + "0x7310243dabd76344326f3659aabda0b846a9c8e9e7d50b990261c14f2b44d560": "0xfba43024a022f9f29d2d2cf5e9194fff93bb68bb51655e64adfee89b84478bea", + "0x7602309c05771de8281f2f3f08e74ec9f19f1bfdaac8cd970d9f7a8d62173b6e": "0x3512d8b5de4b4a0075aed4993bb8b05e3127d98b00ee39fc602a74cf46cbd24f", + "0x892e1120a976151ae5375dca978040abe705c8cbdd3c7b64b41ff1087ee01c22": "0x4bae71c56041d0a73ff376a4a0eea031b54e119e8bf27c36f0383ed2e06affdf", + "0x8a69dcb863773a3bb971a969233286e75cba80abf6ec8d881cada7ea0ad674b0": "0x2610c04393571051ca99aa7e2c62a250466afc6bae8cb3c38ba8a9ea510b6eda", + "0x8d8681a539f3b359e94a05239187dc55cb9cb2dcfe01726d3053573206efe25f": "0x4ec2b03e03ebb3a734ea2172c2c5290c7b569a608342b3b72c2ecdbc6d1bb988", + "0x918d87a20c837fa93ce3089d2cb6386b66f79bbfd0f09bd6784bc52108b69938": "0x4848299ce5b2013b7e0f4ef1835ba4c76fa0152a99128fb4646bf7c3c83b75b7", + "0x9569f58e5e127ceb73049eea8119bb21f8c4328d3b4770da5a67075be3f77c12": "0x42f97fa48dc74283718a0cccc71d31e0932774bfabf4df8d23ece009d7d8bec6", + "0x96f0481f00eeec717fa8c0d41efd04b6e3fdccbebbaa223ee2118449ebea2406": "0x99332fe68ecdf6f6f494eaf54809345648a4d5f9cc907c0e70afdc637432605b", + "0x9c35a3189126603f4b187149f54e09082403dac10b3e899a503e28e897edd6f9": "0x667f256c8ea16b1f4d72d0309a5ee0094e7ec70d68171a3e6105232912c81493", + "0x9db4bc605469ede684fbaa475d320b3fbe6a707deaa953ee1ba29ecb3c1125fb": "0x545d3b883f77604920ed2bbbb67eb6dc98b0ad5d071810eb29336e8fcdc00abc", + "0xb8e0b3cbd7273e83cfdbcb8269e5c3510a28892e815fe28bc0f515be0df2c8f9": "0xbc128fcdfd4757dec6b354cf4d575c3d601562ea7876d6d02adb3038aaef88ff", + "0xc38bb4813ebdb9978d2485ae5ad92e1152f8a955430a63210b69b2277eed9477": "0xdf66fb1b6890b40da5876bc75b0dfb8d5ae91874572df1e46426daed8a40994b", + "0xd75e639b43b1d29e1703abcd2616b6b67f3ae232d53915d472f3a5b6aae6083c": "0x8d0525de5eac7507bb8991adfcfb32efacd77c045266d279a897835e544c702d", + "0xf2644573f57aea10dffac6e7215b409fa19236ae57b735b20b9fac9834391850": "0x35782b6fe6a665598cdd56db1429e4308233b4e8bf375f9445051e18a2cc77e3" }, "Test_UniswapV2DirectToLiquidity": { - "0x174201fc5371e6b5c38ac3e8a150aac595a79929c11ade671773a4af96e796b2": "0xf79f6bfb74c7ea1422de906b5c897cbc4e8e282d391348930a7f75feaa4fc6ce" + "0x6ecef7dad12b92990582b5e7be05c63ac5dd43b74df7e3620cde236d8837442e": "0xc505cbfb2803d1270b897322573cb8b40eea99ed615a365b126e21abd58b3dfb" }, "Test_UniswapV3DirectToLiquidity": { - "0xbbc3774034dce84cb792ad50a090b7c2b736a85af6e59e595e55e4f369523495": "0x2cb699095381555944575ef169da11fe4a944e6c6c382910eefd1a06c32b2c33" + "0x2df6b36ca1e324c0e17ca294f028a7f93d25171e29df7c6cc0db7caeab04d747": "0xb2afbd203a98f50f6ea6d39ce67c89b835b36283b425c5ce45b5c389b0a851de" }, "TokenAllowlist": { "0x09db47d395a68db033a3b222b9d1a212cec8422b03aeafc313aa4d2813c6dd60": "0xe07a005213f35e1f050a4da040d6bc3cae2ad333647b51a6ffa2e7941980043a", From f0df1c4de81a639d93f26013a69e3ca8efc735b1 Mon Sep 17 00:00:00 2001 From: Oighty Date: Tue, 2 Jul 2024 13:54:49 -0400 Subject: [PATCH 09/14] fix: only track ERC6909 supply on derivative contract --- src/modules/Derivative.sol | 7 ++++ src/modules/derivatives/LinearVesting.sol | 32 ++++++++----------- src/modules/derivatives/LinearVestingCard.sol | 2 +- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/modules/Derivative.sol b/src/modules/Derivative.sol index 6aa0bf7f..702185ba 100644 --- a/src/modules/Derivative.sol +++ b/src/modules/Derivative.sol @@ -21,4 +21,11 @@ abstract contract DerivativeModule is IDerivative, ERC6909, ERC6909Metadata, Mod function getTokenMetadata(uint256 tokenId) external view virtual returns (Token memory) { return tokenMetadata[tokenId]; } + + // ========== ERC6909 TOKEN SUPPLY EXTENSION ========== // + + /// @inheritdoc ERC6909Metadata + function totalSupply(uint256 tokenId) public view virtual override returns (uint256) { + return tokenMetadata[tokenId].supply; + } } diff --git a/src/modules/derivatives/LinearVesting.sol b/src/modules/derivatives/LinearVesting.sol index 065fd843..f0685eeb 100644 --- a/src/modules/derivatives/LinearVesting.sol +++ b/src/modules/derivatives/LinearVesting.sol @@ -145,11 +145,6 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { _deployWrapIfNeeded(tokenId_, token_); } - // Increment the supply - unchecked { - token_.supply += amount_; - } - // Transfer collateral token to this contract { ERC20 baseToken = ERC20(token_.underlyingToken); @@ -169,7 +164,12 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { SoulboundCloneERC20 wrappedToken = SoulboundCloneERC20(token_.wrapped); wrappedToken.mint(to_, amount_); } else { - // Otherwise mint the normal derivative token + // Increment the supply + unchecked { + token_.supply += amount_; + } + + // Mint the normal derivative token _mint(to_, tokenId_, amount_); } } @@ -278,9 +278,6 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { } // Burn the wrapped tokens - will be 0 if not wrapped if (wrappedToBurn > 0) { - unchecked { - tokenData.supply -= wrappedToBurn; - } SoulboundCloneERC20(tokenData.wrapped).burn(user_, wrappedToBurn); } @@ -406,7 +403,10 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { if (balanceOf[msg.sender][tokenId_] < amount_) revert InsufficientBalance(); - // Supply does not change + // Decrement supply on this contract, since it's tracked on the ERC20 + unchecked { + tokenMetadata[tokenId_].supply -= amount_; + } // Burn the derivative token _burn(msg.sender, tokenId_, amount_); @@ -437,7 +437,10 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { if (wrappedToken.balanceOf(msg.sender) < amount_) revert InsufficientBalance(); - // Supply does not change + // Increment supply on this contract + unchecked { + token.supply += amount_; + } // Burn the wrapped derivative token wrappedToken.burn(msg.sender, amount_); @@ -743,11 +746,4 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { ); // solhint-enable quotes } - - // ========== ERC6909 TOKEN SUPPLY EXTENSION ========== // - - /// @inheritdoc ERC6909Metadata - function totalSupply(uint256 tokenId_) public view override returns (uint256) { - return tokenMetadata[tokenId_].supply; - } } diff --git a/src/modules/derivatives/LinearVestingCard.sol b/src/modules/derivatives/LinearVestingCard.sol index 8c4ceab9..388fb61a 100644 --- a/src/modules/derivatives/LinearVestingCard.sol +++ b/src/modules/derivatives/LinearVestingCard.sol @@ -42,7 +42,7 @@ contract LinearVestingCard { // ========== ATTRIBUTES ========== // - function _attributes(Info memory tokenInfo) internal view returns (string memory) { + function _attributes(Info memory tokenInfo) internal pure returns (string memory) { return string.concat( '[{"trait_type":"Token ID","value":"', Strings.toString(tokenInfo.tokenId), From 6d0edfba37704bde5becdf27ad9bfc3f0b94379e Mon Sep 17 00:00:00 2001 From: Oighty Date: Tue, 2 Jul 2024 13:55:02 -0400 Subject: [PATCH 10/14] test: linear vesting supply checks --- test/modules/derivatives/LinearVesting.t.sol | 50 +++++++++++++++++++ .../mocks/MockDerivativeModule.sol | 2 + 2 files changed, 52 insertions(+) diff --git a/test/modules/derivatives/LinearVesting.t.sol b/test/modules/derivatives/LinearVesting.t.sol index 7fcbbbc1..959e8d71 100644 --- a/test/modules/derivatives/LinearVesting.t.sol +++ b/test/modules/derivatives/LinearVesting.t.sol @@ -841,6 +841,7 @@ contract LinearVestingTest is Test, Permit2User { assertTrue(wrappedAddress == address(0), "wrappedAddress mismatch"); assertEq(amountCreated, _AMOUNT, "amountCreated mismatch"); assertEq(_linearVesting.balanceOf(_ALICE, tokenId), _AMOUNT, "balanceOf mismatch"); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT, "totalSupply mismatch"); } function test_mint_params_expiryTimestampIsBeforeCurrentTimestamp() @@ -859,6 +860,7 @@ contract LinearVestingTest is Test, Permit2User { assertTrue(wrappedAddress == address(0), "wrappedAddress mismatch"); assertEq(amountCreated, _AMOUNT, "amountCreated mismatch"); assertEq(_linearVesting.balanceOf(_ALICE, tokenId), _AMOUNT, "balanceOf mismatch"); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT, "totalSupply mismatch"); } function test_mint_params_afterExpiry() @@ -880,6 +882,7 @@ contract LinearVestingTest is Test, Permit2User { assertEq(_linearVesting.balanceOf(_ALICE, tokenId), _AMOUNT, "balanceOf mismatch"); assertEq(_underlyingToken.balanceOf(_ALICE), 0, "underlying: balanceOf mismatch"); assertEq(_linearVesting.redeemable(_ALICE, tokenId), _AMOUNT, "redeemable mismatch"); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT, "totalSupply mismatch"); } function test_mint_params_mintAmountIsZero_reverts() public { @@ -905,6 +908,7 @@ contract LinearVestingTest is Test, Permit2User { // Check values assertTrue(tokenId > 0, "tokenId mismatch"); assertEq(_linearVesting.balanceOf(address(0), tokenId), _AMOUNT, "balanceOf mismatch"); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT, "totalSupply mismatch"); } function test_mint_params_insufficentBalance_reverts() public { @@ -953,6 +957,7 @@ contract LinearVestingTest is Test, Permit2User { ); assertEq(_underlyingToken.balanceOf(_ALICE), 0, "underlying: balanceOf mismatch"); assertEq(_linearVesting.redeemable(_ALICE, tokenId), 0, "redeemable mismatch"); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT, "totalSupply mismatch"); } function test_mint_params_afterVestingStart(uint48 elapsed_) @@ -984,6 +989,7 @@ contract LinearVestingTest is Test, Permit2User { expectedRedeemableAmount, "redeemable mismatch" ); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT, "totalSupply mismatch"); } function test_mint_params_givenExistingDerivativeTokens_afterVestingStart(uint48 elapsed_) @@ -1028,6 +1034,7 @@ contract LinearVestingTest is Test, Permit2User { expectedRedeemableAmount, "redeemable mismatch" ); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT + _AMOUNT_TWO, "totalSupply mismatch"); } function test_mint_params_notWrapped_tokenNotDeployed() @@ -1045,6 +1052,7 @@ contract LinearVestingTest is Test, Permit2User { assertTrue(wrappedAddress == address(0), "wrappedAddress mismatch"); assertEq(amountCreated, _AMOUNT, "amountCreated mismatch"); assertEq(_linearVesting.balanceOf(_ALICE, tokenId), _AMOUNT, "balanceOf mismatch"); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT, "totalSupply mismatch"); } function test_mint_params_notWrapped_tokenDeployed() @@ -1063,6 +1071,7 @@ contract LinearVestingTest is Test, Permit2User { assertTrue(wrappedAddress == address(0), "wrappedAddress mismatch"); assertEq(amountCreated, _AMOUNT, "amountCreated mismatch"); assertEq(_linearVesting.balanceOf(_ALICE, tokenId), _AMOUNT, "balanceOf mismatch"); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT, "totalSupply mismatch"); } function test_mint_params_wrapped_wrappedTokenIsNotDeployed() @@ -1083,6 +1092,8 @@ contract LinearVestingTest is Test, Permit2User { assertEq( SoulboundCloneERC20(wrappedAddress).balanceOf(_ALICE), _AMOUNT, "balanceOf mismatch" ); + assertEq(_linearVesting.totalSupply(tokenId), 0, "totalSupply mismatch"); + assertEq(SoulboundCloneERC20(wrappedAddress).totalSupply(), _AMOUNT, "balanceOf mismatch"); } function test_mint_params_wrapped_wrappedTokenIsDeployed() @@ -1103,6 +1114,8 @@ contract LinearVestingTest is Test, Permit2User { assertEq( SoulboundCloneERC20(wrappedAddress).balanceOf(_ALICE), _AMOUNT, "balanceOf mismatch" ); + assertEq(_linearVesting.totalSupply(tokenId), 0, "totalSupply mismatch"); + assertEq(SoulboundCloneERC20(wrappedAddress).totalSupply(), _AMOUNT, "balanceOf mismatch"); } function test_mint_params_notParent() @@ -1123,6 +1136,8 @@ contract LinearVestingTest is Test, Permit2User { assertEq( SoulboundCloneERC20(wrappedAddress).balanceOf(_ALICE), _AMOUNT, "balanceOf mismatch" ); + assertEq(_linearVesting.totalSupply(tokenId), 0, "totalSupply mismatch"); + assertEq(SoulboundCloneERC20(wrappedAddress).totalSupply(), _AMOUNT, "balanceOf mismatch"); } function test_mint_params_notParent_insufficientBalance_reverts() @@ -1196,6 +1211,7 @@ contract LinearVestingTest is Test, Permit2User { // Check values assertEq(_linearVesting.balanceOf(address(0), tokenId), _AMOUNT); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT, "totalSupply mismatch"); } function test_mint_tokenId_insufficentBalance_reverts() public givenDerivativeIsDeployed { @@ -1244,6 +1260,7 @@ contract LinearVestingTest is Test, Permit2User { ); assertEq(_underlyingToken.balanceOf(_ALICE), 0, "underlying: balanceOf mismatch"); assertEq(_linearVesting.redeemable(_ALICE, tokenId), 0, "redeemable mismatch"); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT, "totalSupply mismatch"); } function test_mint_tokenId_afterVestingStart(uint48 elapsed_) @@ -1275,6 +1292,7 @@ contract LinearVestingTest is Test, Permit2User { expectedRedeemableAmount, "redeemable mismatch" ); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT, "totalSupply mismatch"); } function test_mint_tokenId_afterVestingExpiry() @@ -1297,6 +1315,7 @@ contract LinearVestingTest is Test, Permit2User { ); assertEq(_underlyingToken.balanceOf(_ALICE), 0, "underlying: balanceOf mismatch"); assertEq(_linearVesting.redeemable(_ALICE, tokenId), _AMOUNT, "redeemable mismatch"); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT, "totalSupply mismatch"); } function test_mint_tokenId_givenExistingDerivativeTokens_afterVestingStart(uint48 elapsed_) @@ -1340,6 +1359,7 @@ contract LinearVestingTest is Test, Permit2User { expectedRedeemableAmount, "redeemable mismatch" ); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT + _AMOUNT_TWO, "totalSupply mismatch"); } function test_mint_tokenId_givenExistingDerivativeTokens_afterVestingExpiry() @@ -1377,6 +1397,7 @@ contract LinearVestingTest is Test, Permit2User { assertEq( _linearVesting.redeemable(_ALICE, tokenId), _AMOUNT + _AMOUNT_TWO, "redeemable mismatch" ); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT + _AMOUNT_TWO, "totalSupply mismatch"); } function test_mint_tokenId_notWrapped() @@ -1394,6 +1415,7 @@ contract LinearVestingTest is Test, Permit2User { assertTrue(wrappedAddress == address(0)); assertEq(amountCreated, _AMOUNT); assertEq(_linearVesting.balanceOf(_ALICE, tokenId), _AMOUNT, "balanceOf mismatch"); + assertEq(_linearVesting.totalSupply(tokenId), _AMOUNT, "totalSupply mismatch"); } function test_mint_tokenId_wrapped_wrappedTokenIsNotDeployed() @@ -1414,6 +1436,8 @@ contract LinearVestingTest is Test, Permit2User { assertEq( SoulboundCloneERC20(wrappedAddress).balanceOf(_ALICE), _AMOUNT, "balanceOf mismatch" ); + assertEq(_linearVesting.totalSupply(tokenId), 0, "totalSupply mismatch"); + assertEq(SoulboundCloneERC20(wrappedAddress).totalSupply(), _AMOUNT, "balanceOf mismatch"); } function test_mint_tokenId_wrapped_wrappedTokenIsDeployed() @@ -1434,6 +1458,8 @@ contract LinearVestingTest is Test, Permit2User { assertEq( SoulboundCloneERC20(wrappedAddress).balanceOf(_ALICE), _AMOUNT, "balanceOf mismatch" ); + assertEq(_linearVesting.totalSupply(tokenId), 0, "totalSupply mismatch"); + assertEq(SoulboundCloneERC20(wrappedAddress).totalSupply(), _AMOUNT, "balanceOf mismatch"); } function test_mint_tokenId_notParent() @@ -1454,6 +1480,8 @@ contract LinearVestingTest is Test, Permit2User { assertEq( SoulboundCloneERC20(wrappedAddress).balanceOf(_ALICE), _AMOUNT, "balanceOf mismatch" ); + assertEq(_linearVesting.totalSupply(tokenId), 0, "totalSupply mismatch"); + assertEq(SoulboundCloneERC20(wrappedAddress).totalSupply(), _AMOUNT, "balanceOf mismatch"); } // redeem @@ -1538,6 +1566,7 @@ contract LinearVestingTest is Test, Permit2User { // Check values assertEq(_linearVesting.balanceOf(_ALICE, _derivativeTokenId), _AMOUNT - amount); assertEq(SoulboundCloneERC20(_underlyingTokenAddress).balanceOf(_ALICE), amount); + assertEq(_linearVesting.totalSupply(_derivativeTokenId), _AMOUNT - amount); } function test_redeem_givenWrappedBalance(uint256 amount_) @@ -1556,6 +1585,8 @@ contract LinearVestingTest is Test, Permit2User { assertEq(_linearVesting.balanceOf(_ALICE, _derivativeTokenId), 0); assertEq(SoulboundCloneERC20(_derivativeWrappedAddress).balanceOf(_ALICE), _AMOUNT - amount); assertEq(SoulboundCloneERC20(_underlyingTokenAddress).balanceOf(_ALICE), amount); + assertEq(_linearVesting.totalSupply(_derivativeTokenId), 0); + assertEq(SoulboundCloneERC20(_derivativeWrappedAddress).totalSupply(), _AMOUNT - amount); } function test_redeem_givenUnwrappedBalance(uint256 amount_) @@ -1574,6 +1605,8 @@ contract LinearVestingTest is Test, Permit2User { assertEq(_linearVesting.balanceOf(_ALICE, _derivativeTokenId), _AMOUNT - amount); assertEq(SoulboundCloneERC20(_derivativeWrappedAddress).balanceOf(_ALICE), 0); assertEq(SoulboundCloneERC20(_underlyingTokenAddress).balanceOf(_ALICE), amount); + assertEq(_linearVesting.totalSupply(_derivativeTokenId), _AMOUNT - amount); + assertEq(SoulboundCloneERC20(_derivativeWrappedAddress).totalSupply(), 0); } function test_redeem_givenMixedBalance() @@ -1593,6 +1626,8 @@ contract LinearVestingTest is Test, Permit2User { assertEq(_linearVesting.balanceOf(_ALICE, _derivativeTokenId), 0); // Redeems unwrapped first assertEq(SoulboundCloneERC20(_derivativeWrappedAddress).balanceOf(_ALICE), _AMOUNT - 1); assertEq(SoulboundCloneERC20(_underlyingTokenAddress).balanceOf(_ALICE), amountToRedeem); + assertEq(_linearVesting.totalSupply(_derivativeTokenId), 0); + assertEq(SoulboundCloneERC20(_derivativeWrappedAddress).totalSupply(), _AMOUNT - 1); } // redeem max @@ -1643,6 +1678,7 @@ contract LinearVestingTest is Test, Permit2User { // Check values assertEq(_linearVesting.balanceOf(_ALICE, _derivativeTokenId), 0); assertEq(SoulboundCloneERC20(_underlyingTokenAddress).balanceOf(_ALICE), _AMOUNT); + assertEq(_linearVesting.totalSupply(_derivativeTokenId), 0); } function test_redeemMax_givenWrappedBalance_givenVestingExpiry() @@ -1659,6 +1695,8 @@ contract LinearVestingTest is Test, Permit2User { assertEq(_linearVesting.balanceOf(_ALICE, _derivativeTokenId), 0); assertEq(SoulboundCloneERC20(_derivativeWrappedAddress).balanceOf(_ALICE), 0); assertEq(SoulboundCloneERC20(_underlyingTokenAddress).balanceOf(_ALICE), _AMOUNT); + assertEq(_linearVesting.totalSupply(_derivativeTokenId), 0); + assertEq(SoulboundCloneERC20(_derivativeWrappedAddress).totalSupply(), 0); } function test_redeemMax_givenUnwrappedBalance_givenVestingExpiry() @@ -1675,6 +1713,8 @@ contract LinearVestingTest is Test, Permit2User { assertEq(_linearVesting.balanceOf(_ALICE, _derivativeTokenId), 0); assertEq(SoulboundCloneERC20(_derivativeWrappedAddress).balanceOf(_ALICE), 0); assertEq(SoulboundCloneERC20(_underlyingTokenAddress).balanceOf(_ALICE), _AMOUNT); + assertEq(_linearVesting.totalSupply(_derivativeTokenId), 0); + assertEq(SoulboundCloneERC20(_derivativeWrappedAddress).totalSupply(), 0); } function test_redeemMax(uint48 elapsed_) public givenWrappedDerivativeIsDeployed { @@ -1717,6 +1757,16 @@ contract LinearVestingTest is Test, Permit2User { redeemable, "underlying token: balanceOf mismatch" ); + assertEq( + _linearVesting.totalSupply(_derivativeTokenId), + expectedBalanceUnwrapped, + "derivative token: totalSupply mismatch" + ); + assertEq( + SoulboundCloneERC20(_derivativeWrappedAddress).totalSupply(), + expectedBalanceWrapped, + "wrapped derivative token: totalSupply mismatch" + ); } // redeemable diff --git a/test/modules/derivatives/mocks/MockDerivativeModule.sol b/test/modules/derivatives/mocks/MockDerivativeModule.sol index 96610554..dfb1922f 100644 --- a/test/modules/derivatives/mocks/MockDerivativeModule.sol +++ b/test/modules/derivatives/mocks/MockDerivativeModule.sol @@ -93,6 +93,8 @@ contract MockDerivativeModule is DerivativeModule { } // Otherwise mint as normal else { + // Increment the supply + tokenMetadata[tokenId].supply += amount_; derivativeToken.mint(to_, tokenId, outputAmount); } From 47374c03d55bbd43467b2c0501a0a0396a9167c6 Mon Sep 17 00:00:00 2001 From: Oighty Date: Wed, 3 Jul 2024 11:42:56 -0400 Subject: [PATCH 11/14] fix: token URI encoding now in Base64 and other minor updates --- src/modules/derivatives/LinearVesting.sol | 27 ++++++++++++------- src/modules/derivatives/LinearVestingCard.sol | 8 +++--- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/modules/derivatives/LinearVesting.sol b/src/modules/derivatives/LinearVesting.sol index f0685eeb..1fe7961b 100644 --- a/src/modules/derivatives/LinearVesting.sol +++ b/src/modules/derivatives/LinearVesting.sol @@ -8,6 +8,7 @@ import {ClonesWithImmutableArgs} from "src/lib/clones/ClonesWithImmutableArgs.so import {Timestamp} from "src/lib/Timestamp.sol"; import {ERC6909Metadata} from "src/lib/ERC6909Metadata.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {Base64} from "lib/openzeppelin-contracts/contracts/utils/Base64.sol"; import {IDerivative} from "src/interfaces/modules/IDerivative.sol"; import {ILinearVesting} from "src/interfaces/modules/derivatives/ILinearVesting.sol"; @@ -733,16 +734,22 @@ contract LinearVesting is DerivativeModule, ILinearVesting, LinearVestingCard { // Return the token URI // solhint-disable quotes return string.concat( - "data:application/json;utf8,", - '{"name": "', - name(tokenId_), - '", "description": "', - _symbol, - ' Soulbound Linear Vesting Derivative Token. Powered by Axis.", "attributes": ', - _attributes(info), - ', "image": "data:image/svg+xml;utf8,', - _render(info), - '"}' + "data:application/json;base64,", + Base64.encode( + bytes( + string.concat( + '{"name": "', + name(tokenId_), + '", "description": "', + _symbol, + ' Soulbound Linear Vesting Derivative Token. Powered by Axis.", "attributes": ', + _attributes(info), + ', "image": "data:image/svg+xml;base64,', + Base64.encode(bytes(_render(info))), + '"}' + ) + ) + ) ); // solhint-enable quotes } diff --git a/src/modules/derivatives/LinearVestingCard.sol b/src/modules/derivatives/LinearVestingCard.sol index 388fb61a..02027805 100644 --- a/src/modules/derivatives/LinearVestingCard.sol +++ b/src/modules/derivatives/LinearVestingCard.sol @@ -124,8 +124,8 @@ contract LinearVestingCard { return string.concat( svg.text(string.concat('x="145" y="460" font-size="10" ', _TEXT_STYLE), _addrString), svg.text( - string.concat('x="145" y="480" font-size="10" ', _TEXT_STYLE), - string.concat("ID: ", Strings.toString(tokenId)) + string.concat('x="145" y="480" font-size="6" ', _TEXT_STYLE), + string.concat("ID: ", Strings.toHexString(tokenId)) ) ); } @@ -139,7 +139,7 @@ contract LinearVestingCard { { bool started = start <= currentTime; - progress = started ? (currentTime - start) * 100 / (end - start) : 0; + progress = started ? ((currentTime - start) * 100) / (end - start) : 0; // progress can be at most 100 progress = progress > 100 ? 100 : progress; @@ -220,7 +220,7 @@ contract LinearVestingCard { return string.concat( svg.text(string.concat('x="60" y="200" font-size="12" ', _TEXT_STYLE), start), - svg.text(string.concat('x="230" y="200" font-size="12"', _TEXT_STYLE), expiry) + svg.text(string.concat('x="230" y="200" font-size="12" ', _TEXT_STYLE), expiry) ); } From c0b0a6766f41307874edc12f5a9b075aad2fa8c5 Mon Sep 17 00:00:00 2001 From: Oighty Date: Wed, 3 Jul 2024 11:43:17 -0400 Subject: [PATCH 12/14] test: add tests to check live tokenURIs --- test/modules/derivatives/LinearVesting.t.sol | 78 +++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/test/modules/derivatives/LinearVesting.t.sol b/test/modules/derivatives/LinearVesting.t.sol index 959e8d71..1935caec 100644 --- a/test/modules/derivatives/LinearVesting.t.sol +++ b/test/modules/derivatives/LinearVesting.t.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.19; // Libraries import {Test} from "forge-std/Test.sol"; +import {console2} from "forge-std/console2.sol"; import {Permit2User} from "test/lib/permit2/Permit2User.sol"; import {StringHelper} from "test/lib/String.sol"; @@ -53,7 +54,7 @@ contract LinearVestingTest is Test, Permit2User { vm.warp(_VESTING_START - 1); _underlyingToken = - new MockFeeOnTransferERC20("Underlying", "UNDERLYING", _underlyingTokenDecimals); + new MockFeeOnTransferERC20("Underlying", "UNDY", _underlyingTokenDecimals); _underlyingTokenAddress = address(_underlyingToken); _auctionHouse = new AtomicAuctionHouse(address(this), _PROTOCOL, _permit2Address); @@ -65,7 +66,7 @@ contract LinearVestingTest is Test, Permit2User { _vestingParamsBytes = abi.encode(_vestingParams); _wrappedDerivativeTokenName = "Underlying 2024-01-12"; - _wrappedDerivativeTokenSymbol = "UNDERLYING 2024-01-12"; + _wrappedDerivativeTokenSymbol = "UNDY 2024-01-12"; _wrappedDerivativeTokenNameLength = bytes(_wrappedDerivativeTokenName).length; _wrappedDerivativeTokenSymbolLength = bytes(_wrappedDerivativeTokenSymbol).length; } @@ -2543,4 +2544,77 @@ contract LinearVestingTest is Test, Permit2User { // Call _linearVesting.exercise(_derivativeTokenId, _AMOUNT); } + + // tokenURI + // [X] given the vesting period hasn't started + // [X] the progress bar is grey (verified externally) + // [X] given the vesting period has just started + // [X] the progress bar has blue green gradient at the left bar (verified externally) + // [X] given the vesting period is halfway through + // [X] the progress bar has blue green gradient in the middle and a circle showing current point (verified externally) + // [X] given the vesting period has ended + // [X] the progress bar is fully blue green gradient (verified externally) + + function test_tokenURI_beforeStart() + public + givenDerivativeIsDeployed + givenAliceHasDerivativeTokens(_AMOUNT) + { + // Call + string memory tokenURI = _linearVesting.tokenURI(_derivativeTokenId); + + console2.log("beforeStart: ", tokenURI); + } + + function test_tokenURI_atStart() + public + givenDerivativeIsDeployed + givenAliceHasDerivativeTokens(_AMOUNT) + { + vm.warp(_VESTING_START); + + // Call + string memory tokenURI = _linearVesting.tokenURI(_derivativeTokenId); + + console2.log("atStart: ", tokenURI); + } + + function test_tokenURI_halfway() + public + givenDerivativeIsDeployed + givenAliceHasDerivativeTokens(_AMOUNT) + { + vm.warp(_VESTING_START + _VESTING_DURATION / 2); + + // Call + string memory tokenURI = _linearVesting.tokenURI(_derivativeTokenId); + + console2.log("halfway: ", tokenURI); + } + + function test_tokenURI_atEnd() + public + givenDerivativeIsDeployed + givenAliceHasDerivativeTokens(_AMOUNT) + { + vm.warp(_VESTING_START + _VESTING_DURATION); + + // Call + string memory tokenURI = _linearVesting.tokenURI(_derivativeTokenId); + + console2.log("atEnd: ", tokenURI); + } + + function test_tokenURI_afterEnd() + public + givenDerivativeIsDeployed + givenAliceHasDerivativeTokens(_AMOUNT) + { + vm.warp(_VESTING_START + _VESTING_DURATION * 2); + + // Call + string memory tokenURI = _linearVesting.tokenURI(_derivativeTokenId); + + console2.log("afterEnd: ", tokenURI); + } } From bf5bc6ba1325457f6807c62f4cfe3e63d88315c8 Mon Sep 17 00:00:00 2001 From: Oighty Date: Wed, 3 Jul 2024 11:49:24 -0400 Subject: [PATCH 13/14] test: update salts and fix assertion that was broken --- script/salts/salts.json | 4 ++-- test/modules/derivatives/LinearVesting.t.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/script/salts/salts.json b/script/salts/salts.json index aad592b5..136332ae 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -74,10 +74,10 @@ "0xf2644573f57aea10dffac6e7215b409fa19236ae57b735b20b9fac9834391850": "0x35782b6fe6a665598cdd56db1429e4308233b4e8bf375f9445051e18a2cc77e3" }, "Test_UniswapV2DirectToLiquidity": { - "0x6ecef7dad12b92990582b5e7be05c63ac5dd43b74df7e3620cde236d8837442e": "0xc505cbfb2803d1270b897322573cb8b40eea99ed615a365b126e21abd58b3dfb" + "0x2b19228bea571f0f8d492cc276ce2765f12958a42d00d40c0bb87f414c88410b": "0x4502811f276078d57f96c0af0179adb80a2b6c5ffeb2d539cc2c26b6cc850f87" }, "Test_UniswapV3DirectToLiquidity": { - "0x2df6b36ca1e324c0e17ca294f028a7f93d25171e29df7c6cc0db7caeab04d747": "0xb2afbd203a98f50f6ea6d39ce67c89b835b36283b425c5ce45b5c389b0a851de" + "0xebe2a2742c8a71eef5d3b2bb8cd69234c344f9e6baefbfa8aaa09d4656953301": "0x87c9dc3c09384859be11af9736f1cbe285b2c61ee8d18fb8ce300fe967a41830" }, "TokenAllowlist": { "0x09db47d395a68db033a3b222b9d1a212cec8422b03aeafc313aa4d2813c6dd60": "0xe07a005213f35e1f050a4da040d6bc3cae2ad333647b51a6ffa2e7941980043a", diff --git a/test/modules/derivatives/LinearVesting.t.sol b/test/modules/derivatives/LinearVesting.t.sol index 1935caec..ae8f2361 100644 --- a/test/modules/derivatives/LinearVesting.t.sol +++ b/test/modules/derivatives/LinearVesting.t.sol @@ -125,7 +125,7 @@ contract LinearVestingTest is Test, Permit2User { _vestingParamsBytes = abi.encode(_vestingParams); _wrappedDerivativeTokenName = "Underlying 2024-01-14"; - _wrappedDerivativeTokenSymbol = "UNDERLYING 2024-01-14"; + _wrappedDerivativeTokenSymbol = "UNDY 2024-01-14"; _wrappedDerivativeTokenNameLength = bytes(_wrappedDerivativeTokenName).length; _wrappedDerivativeTokenSymbolLength = bytes(_wrappedDerivativeTokenSymbol).length; _; From f4e2388a46f69819abdc2bd68f873949dbf1361d Mon Sep 17 00:00:00 2001 From: Oighty Date: Mon, 8 Jul 2024 13:37:36 -0400 Subject: [PATCH 14/14] test: update salts --- script/salts/salts.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/script/salts/salts.json b/script/salts/salts.json index a776f567..0f7c1e1e 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -50,12 +50,12 @@ "0xed4ba02077e70f78db70ce191509333d2dcccfff136d30dcb011f567305e88ad": "0x2b8bc588174d409692f1bd993942e3806c9a11344dbe674f46aed32a030aacf1" }, "Test_AllocatedMerkleAllowlist": { - "0xb17794250569bcd64aae2ec1312399120db92167bf0a2602821e0ee953dd3d31": "0xa8a4fc4fb8d721bdce27342cdd381eb510362146f49e56698a8bcb6c639306ff", - "0xecbee26b3d0e662be84e4cef678b23166957f507ca68f32eaa49e60ceb567981": "0x1598e58155ab6f6f2d8b2813c5c763f04ab980c3e65dffe2b3a312e8ccba4ae7" + "0x8de6e7b0b1f6974167a896ad8d377c8c29ad0132dd3f7b26f3aae7d4bb348d2f": "0xbafe0e1369edea3d992723bf3ff6a352dafbbb9f4dd6b81d52da4b538c123bb5", + "0xce7f9e37dca3a16d5c26ba67c9eba2e889ff76c1d27a05ed9ebd75083d46e241": "0x9c9a73570423fffe4c11df49d5434528af493273cd7424ec18e474b7940627ed" }, "Test_CappedMerkleAllowlist": { - "0xa0ed568f98efa2efd4e93f21a5445970919a333349a8f5369a28ae93271d9cca": "0x6fb3e2a45254801d17269019f8afcadbd132d417f4dde1bfc65dd8f440256a56", - "0xfd2c28cb6c38931a1f784078c41d21d7bf8fc3653231edca225a5fbbc50529dc": "0x88a482ab0eacd2d6c1fc7b3858bfba29aa92b8abfc6239e02e6952640c3ebdf7" + "0x5abd4cf6f12f56a1077d607918110659db02bbb5f419e4f4643c926e7c78d701": "0x7e5435b4313ad9ef6d831b39306df989156afac6ebeb1c4644cd7bd63088bfb7", + "0xe577ed57cc6c3ce09f0d1f66fbf3fcb30b35fde4c350c8d515d0f930603c41df": "0x97167e0f92b3e91d6e0fb8637c5ddfa224a3ec191528f46208663258c0cb95c3" }, "Test_GUniFactory": { "0xa8ee5069b769099da4dbc8c5d4c95cb74d416554203119678be4d0eba33f5962": "0x767e6faad4f8e62d6ab897930a0eb497cb71ebc75f90856dc13996fb00b907d3" @@ -85,13 +85,13 @@ "0x78a7c23f669f5df656575693f45f928b7af2ed4bc981cf6dec77bef2c03bb081": "0xf0bc4482fcf2593df36fb9fb83ee5d273e7ee6a3caa9414d9c24eeee96567b6f" }, "Test_UniswapV2DirectToLiquidity": { - "0x9c33aa54294ebd7a8909181ebd6aed2b967b0eb4b0fb95467495624e6c211a67": "0xba73f6582c497ae5c4d38c53ba8bfb98266aa3566aaafef6d00f65d327effd66" + "0xf108beb959acb1e7e41cab1e1a3039f077c572167a9dadf5c31b642d80bc8c3c": "0x776bb071737d0bd3c0c1096a8a9d83574280b883b2681dadffaf6fa94fc669fc" }, "Test_UniswapV2Router": { "0xe6204aa211921628df345ff0b909c863298684cd8ca65de5479af6b70662e622": "0xf5700f795856223d45b9e1b76fa0f23db9d770b25c44cae8f556ae6d1e7bee5e" }, "Test_UniswapV3DirectToLiquidity": { - "0xca7cd77c52253f2e1eec58c25dbb3b89ccaa938974767ed31217819f71d2db06": "0xdb4b3fa1df0a6205daaeb852a4b456a86df10140fd1d479072f7c62ea9ba7a79" + "0x5aec331792b9b995051d0efd683176c0800e527130eb7514f415c4f1a9f09f43": "0x37c69f7eea77a282f753c9ae1505d0272745cd870612e78e4e962285eb0de2d3" }, "Test_UniswapV3Factory": { "0xbcd657c3390ecd2e1782b6473400c51fa124922eb98b69f1b5192eb0f8e3d3df": "0xfad08a6eaa7974f06cec79e15064de7f6fa17b22ecdb3764988b2c4b1571c613"