A Prediction Market on the AT Protocol

feat(app.tsx): chart demo

Ciaran ad7686ef f5b4e5c3

+492 -33
+73
bun.lock
··· 29 29 "lucide-react": "^0.577.0", 30 30 "pg": "^8.19.0", 31 31 "radix-ui": "^1.4.3", 32 + "recharts": "2.15.4", 32 33 "tailwind-merge": "^3.5.0", 33 34 "tailwindcss": "^4.2.1", 34 35 "usehooks-ts": "^3.1.1", ··· 168 169 "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw=="], 169 170 170 171 "@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="], 172 + 173 + "@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="], 171 174 172 175 "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], 173 176 ··· 627 630 628 631 "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], 629 632 633 + "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], 634 + 635 + "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], 636 + 637 + "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="], 638 + 639 + "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="], 640 + 641 + "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="], 642 + 643 + "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], 644 + 645 + "@types/d3-shape": ["@types/d3-shape@3.1.8", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="], 646 + 647 + "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], 648 + 649 + "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], 650 + 630 651 "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], 631 652 632 653 "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], ··· 757 778 758 779 "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], 759 780 781 + "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], 782 + 783 + "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], 784 + 785 + "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], 786 + 787 + "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="], 788 + 789 + "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], 790 + 791 + "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], 792 + 793 + "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], 794 + 795 + "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], 796 + 797 + "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], 798 + 799 + "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], 800 + 801 + "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], 802 + 760 803 "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], 761 804 762 805 "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="], 763 806 764 807 "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], 808 + 809 + "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="], 765 810 766 811 "dedent": ["dedent@1.7.2", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA=="], 767 812 ··· 781 826 782 827 "diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], 783 828 829 + "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="], 830 + 784 831 "dotenv": ["dotenv@17.3.1", "", {}, "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA=="], 785 832 786 833 "drizzle-kit": ["drizzle-kit@0.31.9", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-GViD3IgsXn7trFyBUUHyTFBpH/FsHTxYJ66qdbVggxef4UBPHRYxQaRzYLTuekYnk9i5FIEL9pbBIwMqX/Uwrg=="], ··· 835 882 836 883 "event-target-polyfill": ["event-target-polyfill@0.0.4", "", {}, "sha512-Gs6RLjzlLRdT8X9ZipJdIZI/Y6/HhRLyq9RdDlCsnpxr/+Nn6bU2EFGuC94GjxqhM+Nmij2Vcq98yoHrU8uNFQ=="], 837 884 885 + "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], 886 + 838 887 "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], 839 888 840 889 "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], ··· 852 901 "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="], 853 902 854 903 "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], 904 + 905 + "fast-equals": ["fast-equals@5.4.0", "", {}, "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw=="], 855 906 856 907 "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], 857 908 ··· 939 990 940 991 "inherits": ["inherits@2.0.3", "", {}, "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="], 941 992 993 + "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], 994 + 942 995 "ip-address": ["ip-address@10.0.1", "", {}, "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA=="], 943 996 944 997 "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], ··· 1026 1079 "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.31.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw=="], 1027 1080 1028 1081 "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], 1082 + 1083 + "lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="], 1029 1084 1030 1085 "lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="], 1031 1086 1032 1087 "log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="], 1033 1088 1089 + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], 1090 + 1034 1091 "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], 1035 1092 1036 1093 "lucide-react": ["lucide-react@0.577.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A=="], ··· 1173 1230 1174 1231 "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], 1175 1232 1233 + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], 1234 + 1176 1235 "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], 1177 1236 1178 1237 "qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="], ··· 1189 1248 1190 1249 "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], 1191 1250 1251 + "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], 1252 + 1192 1253 "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], 1193 1254 1194 1255 "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], 1195 1256 1196 1257 "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], 1197 1258 1259 + "react-smooth": ["react-smooth@4.0.4", "", { "dependencies": { "fast-equals": "^5.0.1", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q=="], 1260 + 1198 1261 "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], 1199 1262 1263 + "react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="], 1264 + 1200 1265 "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], 1266 + 1267 + "recharts": ["recharts@2.15.4", "", { "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", "lodash": "^4.17.21", "react-is": "^18.3.1", "react-smooth": "^4.0.4", "recharts-scale": "^0.4.4", "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" }, "peerDependencies": { "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw=="], 1268 + 1269 + "recharts-scale": ["recharts-scale@0.4.5", "", { "dependencies": { "decimal.js-light": "^2.4.1" } }, "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w=="], 1201 1270 1202 1271 "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], 1203 1272 ··· 1367 1436 1368 1437 "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], 1369 1438 1439 + "victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="], 1440 + 1370 1441 "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], 1371 1442 1372 1443 "vitest": ["vitest@4.0.18", "", { "dependencies": { "@vitest/expect": "4.0.18", "@vitest/mocker": "4.0.18", "@vitest/pretty-format": "4.0.18", "@vitest/runner": "4.0.18", "@vitest/snapshot": "4.0.18", "@vitest/spy": "4.0.18", "@vitest/utils": "4.0.18", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.10.0", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.0.18", "@vitest/browser-preview": "4.0.18", "@vitest/browser-webdriverio": "4.0.18", "@vitest/ui": "4.0.18", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ=="], ··· 1462 1533 "postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 1463 1534 1464 1535 "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], 1536 + 1537 + "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], 1465 1538 1466 1539 "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], 1467 1540
+7 -5
index.ts
··· 1 1 import type { ActorIdentifier } from "@atcute/lexicons"; 2 - import { createBet, createMarket, createResolution } from "./src/core"; 2 + import { createBet, createMarket } from "./src/core"; 3 3 import { Client } from '@atcute/client'; 4 4 import { PasswordSession } from "@atcute/password-session"; 5 5 import { ComAtprotoRepoStrongRef } from "@atcute/atproto"; ··· 27 27 } 28 28 console.log("marketRef", marketRef); 29 29 30 - const betRecord = await createBet(marketRef, "yes", creds.handle, client); 31 - console.log("bet", betRecord); 30 + for (let i = 0; i < 30; i++) { 31 + const position = Math.random() > 0.5 ? "yes" : "no"; 32 + const bet = await createBet(marketRef, position, creds.handle, client); 33 + console.log(`Seeded bet ${i + 1}/30 (${position})`, bet.uri); 34 + } 32 35 33 - const resolutionRecord = await createResolution(marketRef, "yes", creds.handle, client); 34 - console.log("resolution", resolutionRecord); 36 + console.log("Done seeding 30 bets.");
+1
package.json
··· 66 66 "lucide-react": "^0.577.0", 67 67 "pg": "^8.19.0", 68 68 "radix-ui": "^1.4.3", 69 + "recharts": "2.15.4", 69 70 "tailwind-merge": "^3.5.0", 70 71 "tailwindcss": "^4.2.1", 71 72 "usehooks-ts": "^3.1.1"
+49 -28
src/web/app.tsx
··· 1 1 import { useCumulus } from "./providers/useCumulus"; 2 - import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./components/ui/card"; 3 - import { formatDistance } from "date-fns" 2 + import { formatDistance, getUnixTime } from "date-fns" 4 3 import { Spinner } from "./components/ui/spinner"; 4 + import { LineChart, Line, XAxis, CartesianGrid } from "recharts"; 5 + import { ChartContainer, ChartTooltip } from "./components/ui/chart"; 6 + import { noPrice, yesPrice } from "./lib/lmsr"; 5 7 6 8 export default function App() { 7 - return <div className="p-4 space-y-4"> 8 - <h2>Markets</h2> 9 - <Markets /> 10 - </div> 11 - } 9 + const { markets } = useCumulus(); 10 + 11 + if (markets.isLoading) return <Spinner className='m-auto' /> 12 12 13 - function Markets() { 13 + return <div className="grid md:grid-cols-2 gap-2"> 14 + {markets.data?.map(market => { 15 + let [yes, no] = [0, 0]; 16 + let mappedBets = market.bets 17 + ?.sort((a, b) => a.createdAt > b.createdAt ? 1 : 0) 18 + .map(bet => { 19 + if (bet.position === "yes") yes++; 20 + if (bet.position === "no") no++; 21 + return { 22 + ...bet, 23 + createdAt: formatDistance(bet.createdAt, new Date(), { addSuffix: true }), 24 + timestamp: getUnixTime(bet.createdAt), 25 + yes, 26 + no, 27 + yesPrice: yesPrice(yes, no, market.liquidity), 28 + testNoPrice: 1-yesPrice(yes,no,market.liquidity), 29 + noPrice: noPrice(yes, no, market.liquidity), 30 + } 31 + }) 14 32 15 - const { markets } = useCumulus(); 16 - 17 - if (markets.isLoading) { 18 - return <Card> 19 - <Spinner className='m-auto' /> 20 - </Card> 21 - } 22 33 23 - return <> 24 - {markets.data?.map(market => (<Card key={market.cid}> 25 - <CardHeader> 26 - <CardTitle>{market.question}</CardTitle> 27 - <CardDescription>Closes {formatDistance(new Date(market.closesAt), new Date(), { addSuffix: true })}</CardDescription> 28 - </CardHeader> 29 - <CardContent> 30 - <p>Resolution: {market.resolution?.answer.toUpperCase()}</p> 31 - <p>Bets: {market.bets?.length}</p> 32 - </CardContent> 33 - </Card> 34 - ))} 35 - </> 34 + return <div key={market.cid}> 35 + <h2>{market.question}</h2> 36 + <p>Closes {formatDistance(new Date(market.closesAt), new Date(), { addSuffix: true })}</p> 37 + <p>{market.bets?.length} Positions</p> 38 + <ChartContainer 39 + className="border-2 rounded-lg" 40 + config={{ 41 + yes: { label: "Yes" }, no: { label: "No" } 42 + }}> 43 + <LineChart data={mappedBets}> 44 + <CartesianGrid strokeDasharray="3 3" /> 45 + <ChartTooltip /> 46 + <XAxis dataKey="createdAt" interval={8} /> 47 + <Line dataKey="yes" stroke="var(--color-shell-600)" /> 48 + <Line dataKey="no" stroke="var(--color-coral-600)" /> 49 + <Line dataKey="yesPrice" stroke="var(--color-coral-600)" /> 50 + <Line dataKey="testNoPrice" stroke="var(--color-coral-600)" /> 51 + <Line dataKey="noPrice" stroke="var(--color-coral-600)" /> 52 + </LineChart> 53 + </ChartContainer> 54 + </div> 55 + })} 56 + </div> 36 57 }
+355
src/web/components/ui/chart.tsx
··· 1 + import * as React from "react" 2 + import * as RechartsPrimitive from "recharts" 3 + 4 + import { cn } from "@/web/lib/utils" 5 + 6 + // Format: { THEME_NAME: CSS_SELECTOR } 7 + const THEMES = { light: "", dark: ".dark" } as const 8 + 9 + export type ChartConfig = { 10 + [k in string]: { 11 + label?: React.ReactNode 12 + icon?: React.ComponentType 13 + } & ( 14 + | { color?: string; theme?: never } 15 + | { color?: never; theme: Record<keyof typeof THEMES, string> } 16 + ) 17 + } 18 + 19 + type ChartContextProps = { 20 + config: ChartConfig 21 + } 22 + 23 + const ChartContext = React.createContext<ChartContextProps | null>(null) 24 + 25 + function useChart() { 26 + const context = React.useContext(ChartContext) 27 + 28 + if (!context) { 29 + throw new Error("useChart must be used within a <ChartContainer />") 30 + } 31 + 32 + return context 33 + } 34 + 35 + function ChartContainer({ 36 + id, 37 + className, 38 + children, 39 + config, 40 + ...props 41 + }: React.ComponentProps<"div"> & { 42 + config: ChartConfig 43 + children: React.ComponentProps< 44 + typeof RechartsPrimitive.ResponsiveContainer 45 + >["children"] 46 + }) { 47 + const uniqueId = React.useId() 48 + const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` 49 + 50 + return ( 51 + <ChartContext.Provider value={{ config }}> 52 + <div 53 + data-slot="chart" 54 + data-chart={chartId} 55 + className={cn( 56 + "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden", 57 + className 58 + )} 59 + {...props} 60 + > 61 + <ChartStyle id={chartId} config={config} /> 62 + <RechartsPrimitive.ResponsiveContainer> 63 + {children} 64 + </RechartsPrimitive.ResponsiveContainer> 65 + </div> 66 + </ChartContext.Provider> 67 + ) 68 + } 69 + 70 + const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { 71 + const colorConfig = Object.entries(config).filter( 72 + ([, config]) => config.theme || config.color 73 + ) 74 + 75 + if (!colorConfig.length) { 76 + return null 77 + } 78 + 79 + return ( 80 + <style 81 + dangerouslySetInnerHTML={{ 82 + __html: Object.entries(THEMES) 83 + .map( 84 + ([theme, prefix]) => ` 85 + ${prefix} [data-chart=${id}] { 86 + ${colorConfig 87 + .map(([key, itemConfig]) => { 88 + const color = 89 + itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || 90 + itemConfig.color 91 + return color ? ` --color-${key}: ${color};` : null 92 + }) 93 + .join("\n")} 94 + } 95 + ` 96 + ) 97 + .join("\n"), 98 + }} 99 + /> 100 + ) 101 + } 102 + 103 + const ChartTooltip = RechartsPrimitive.Tooltip 104 + 105 + function ChartTooltipContent({ 106 + active, 107 + payload, 108 + className, 109 + indicator = "dot", 110 + hideLabel = false, 111 + hideIndicator = false, 112 + label, 113 + labelFormatter, 114 + labelClassName, 115 + formatter, 116 + color, 117 + nameKey, 118 + labelKey, 119 + }: React.ComponentProps<typeof RechartsPrimitive.Tooltip> & 120 + React.ComponentProps<"div"> & { 121 + hideLabel?: boolean 122 + hideIndicator?: boolean 123 + indicator?: "line" | "dot" | "dashed" 124 + nameKey?: string 125 + labelKey?: string 126 + }) { 127 + const { config } = useChart() 128 + 129 + const tooltipLabel = React.useMemo(() => { 130 + if (hideLabel || !payload?.length) { 131 + return null 132 + } 133 + 134 + const [item] = payload 135 + const key = `${labelKey || item?.dataKey || item?.name || "value"}` 136 + const itemConfig = getPayloadConfigFromPayload(config, item, key) 137 + const value = 138 + !labelKey && typeof label === "string" 139 + ? config[label as keyof typeof config]?.label || label 140 + : itemConfig?.label 141 + 142 + if (labelFormatter) { 143 + return ( 144 + <div className={cn("font-medium", labelClassName)}> 145 + {labelFormatter(value, payload)} 146 + </div> 147 + ) 148 + } 149 + 150 + if (!value) { 151 + return null 152 + } 153 + 154 + return <div className={cn("font-medium", labelClassName)}>{value}</div> 155 + }, [ 156 + label, 157 + labelFormatter, 158 + payload, 159 + hideLabel, 160 + labelClassName, 161 + config, 162 + labelKey, 163 + ]) 164 + 165 + if (!active || !payload?.length) { 166 + return null 167 + } 168 + 169 + const nestLabel = payload.length === 1 && indicator !== "dot" 170 + 171 + return ( 172 + <div 173 + className={cn( 174 + "grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl", 175 + className 176 + )} 177 + > 178 + {!nestLabel ? tooltipLabel : null} 179 + <div className="grid gap-1.5"> 180 + {payload 181 + .filter((item) => item.type !== "none") 182 + .map((item, index) => { 183 + const key = `${nameKey || item.name || item.dataKey || "value"}` 184 + const itemConfig = getPayloadConfigFromPayload(config, item, key) 185 + const indicatorColor = color || item.payload.fill || item.color 186 + 187 + return ( 188 + <div 189 + key={item.dataKey} 190 + className={cn( 191 + "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground", 192 + indicator === "dot" && "items-center" 193 + )} 194 + > 195 + {formatter && item?.value !== undefined && item.name ? ( 196 + formatter(item.value, item.name, item, index, item.payload) 197 + ) : ( 198 + <> 199 + {itemConfig?.icon ? ( 200 + <itemConfig.icon /> 201 + ) : ( 202 + !hideIndicator && ( 203 + <div 204 + className={cn( 205 + "shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)", 206 + { 207 + "h-2.5 w-2.5": indicator === "dot", 208 + "w-1": indicator === "line", 209 + "w-0 border-[1.5px] border-dashed bg-transparent": 210 + indicator === "dashed", 211 + "my-0.5": nestLabel && indicator === "dashed", 212 + } 213 + )} 214 + style={ 215 + { 216 + "--color-bg": indicatorColor, 217 + "--color-border": indicatorColor, 218 + } as React.CSSProperties 219 + } 220 + /> 221 + ) 222 + )} 223 + <div 224 + className={cn( 225 + "flex flex-1 justify-between leading-none", 226 + nestLabel ? "items-end" : "items-center" 227 + )} 228 + > 229 + <div className="grid gap-1.5"> 230 + {nestLabel ? tooltipLabel : null} 231 + <span className="text-muted-foreground"> 232 + {itemConfig?.label || item.name} 233 + </span> 234 + </div> 235 + {item.value && ( 236 + <span className="font-mono font-medium text-foreground tabular-nums"> 237 + {item.value.toLocaleString()} 238 + </span> 239 + )} 240 + </div> 241 + </> 242 + )} 243 + </div> 244 + ) 245 + })} 246 + </div> 247 + </div> 248 + ) 249 + } 250 + 251 + const ChartLegend = RechartsPrimitive.Legend 252 + 253 + function ChartLegendContent({ 254 + className, 255 + hideIcon = false, 256 + payload, 257 + verticalAlign = "bottom", 258 + nameKey, 259 + }: React.ComponentProps<"div"> & 260 + Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & { 261 + hideIcon?: boolean 262 + nameKey?: string 263 + }) { 264 + const { config } = useChart() 265 + 266 + if (!payload?.length) { 267 + return null 268 + } 269 + 270 + return ( 271 + <div 272 + className={cn( 273 + "flex items-center justify-center gap-4", 274 + verticalAlign === "top" ? "pb-3" : "pt-3", 275 + className 276 + )} 277 + > 278 + {payload 279 + .filter((item) => item.type !== "none") 280 + .map((item) => { 281 + const key = `${nameKey || item.dataKey || "value"}` 282 + const itemConfig = getPayloadConfigFromPayload(config, item, key) 283 + 284 + return ( 285 + <div 286 + key={item.value} 287 + className={cn( 288 + "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground" 289 + )} 290 + > 291 + {itemConfig?.icon && !hideIcon ? ( 292 + <itemConfig.icon /> 293 + ) : ( 294 + <div 295 + className="h-2 w-2 shrink-0 rounded-[2px]" 296 + style={{ 297 + backgroundColor: item.color, 298 + }} 299 + /> 300 + )} 301 + {itemConfig?.label} 302 + </div> 303 + ) 304 + })} 305 + </div> 306 + ) 307 + } 308 + 309 + // Helper to extract item config from a payload. 310 + function getPayloadConfigFromPayload( 311 + config: ChartConfig, 312 + payload: unknown, 313 + key: string 314 + ) { 315 + if (typeof payload !== "object" || payload === null) { 316 + return undefined 317 + } 318 + 319 + const payloadPayload = 320 + "payload" in payload && 321 + typeof payload.payload === "object" && 322 + payload.payload !== null 323 + ? payload.payload 324 + : undefined 325 + 326 + let configLabelKey: string = key 327 + 328 + if ( 329 + key in payload && 330 + typeof payload[key as keyof typeof payload] === "string" 331 + ) { 332 + configLabelKey = payload[key as keyof typeof payload] as string 333 + } else if ( 334 + payloadPayload && 335 + key in payloadPayload && 336 + typeof payloadPayload[key as keyof typeof payloadPayload] === "string" 337 + ) { 338 + configLabelKey = payloadPayload[ 339 + key as keyof typeof payloadPayload 340 + ] as string 341 + } 342 + 343 + return configLabelKey in config 344 + ? config[configLabelKey] 345 + : config[key as keyof typeof config] 346 + } 347 + 348 + export { 349 + ChartContainer, 350 + ChartTooltip, 351 + ChartTooltipContent, 352 + ChartLegend, 353 + ChartLegendContent, 354 + ChartStyle, 355 + }
+7
src/web/lib/lmsr.ts
··· 1 + export function yesPrice(yes: number, no: number, liquidity: number) { 2 + return 1 / (1 + Math.exp((no - yes) / liquidity)); 3 + } 4 + 5 + export function noPrice(yes: number, no: number, liquidity: number) { 6 + return 1 / (1 + Math.exp((yes - no) / liquidity)); 7 + }