diff --git a/README.md b/README.md index ab7109a62..1b2c88e5b 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,24 @@ npm install && npm run dev - All articles are markdown and stored in `/src/content/docs/`. - Navigation is JSON in `/src/config/sidebar.ts` +## Tooling on Scroll + +If you'd like to add an entry to our [tooling list](http://docs.scroll.xyz/en/developers/scroll-contracts), create a PR to add a new `mdx` file in the [tooling content folder](src/content/tools), using the following template. You can also refer to other existing entries for reference. + +``` +--- +name: "Safe" +category: ["Identity", "Wallet"] +excerpt: "Safe allows you to create smart wallet on chain." +logo: { src: "https://app.safe.global/images/safe-logo-green.png", alt: "Safe Logo" } +website: "https://app.safe.global" +network: ["Mainnet", "Testnet] +noAdditionalInfo: false +--- + +Add additional info here about how to access this tool on Scroll (ex. contract addresses, tutorials, API URLs) +``` + ## Credits - Special thanks to the Chainlink team whose documentation we forked. Their repo is available [here](https://github.com/smartcontractkit/documentation) and viewable at [https://docs.chain.link/](https://docs.chain.link/). diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 5af7f0f46..0d2ff57bb 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -89,6 +89,7 @@ "erc1155TokenBridge": "ERC1155 Token Bridge", "theScrollMessenger": "The Scroll Messenger", "transactionFeesOnScroll": "Transaction Fees on Scroll", + "toolingDeployedOnScroll": "Tooling deployed On Scroll", "l2Fee": "L2 Fee", "l1Fee": "L1 Fee", "gasOracle": "Gas Oracle", diff --git a/src/components/LinesEllipsis/index.tsx b/src/components/LinesEllipsis/index.tsx new file mode 100644 index 000000000..e6a6bd0a0 --- /dev/null +++ b/src/components/LinesEllipsis/index.tsx @@ -0,0 +1,164 @@ +import { useState, useEffect, useRef } from "preact/hooks" + +const agentStyle = { + position: "absolute", + bottom: 0, + left: 0, + height: 0, + overflow: "hidden", + paddingTop: 0, + paddingBottom: 0, + border: "none", +} + +const mirrorProps = [ + "box-sizing", + "width", + "font-size", + "font-weight", + "font-family", + "font-style", + "letter-spacing", + "text-indent", + "white-space", + "word-break", + "overflow-wrap", + "padding-left", + "padding-right", +] + +function prevSibling(node, count) { + while (node && count--) { + node = node.previousElementSibling + } + return node +} + +const LinesEllipsis = props => { + const { component: Component = "div", ellipsis, trimRight = true, basedOn, maxLine = 1, text, className, onReflow, ...rest } = props + + const [displayedText, setDisplayedText] = useState(text) + const [clamped, setClamped] = useState(false) + + const units = useRef([]) + const shadowRef = useRef() + const targetRef = useRef() + const ellipsisRef = useRef() + + useEffect(() => { + const handleSizeChanged = entries => { + if (targetRef.current) { + copyStyleToShadow() + reflow({ basedOn, text, maxLine }) + } + } + const resizeObserver = new ResizeObserver(handleSizeChanged) + resizeObserver.observe(targetRef.current) + + return () => { + if (targetRef.current) { + resizeObserver && resizeObserver.unobserve(targetRef.current) + } + } + }, [basedOn, text, maxLine]) + + const copyStyleToShadow = () => { + const targetStyle = window.getComputedStyle(targetRef.current) + mirrorProps.forEach(key => { + shadowRef.current.style[key] = targetStyle[key] + }) + } + + const reflow = props => { + /* eslint-disable no-control-regex */ + const basedOn = props.basedOn || (/^[\x00-\x7F]+$/.test(props.text) ? "words" : "letters") + + if (basedOn === "words") { + units.current = props.text.split(/\b|(?=\W)/) + } else if (basedOn === "letters") { + units.current = Array.from(props.text) + } else { + // default + units.current = props.text.split(/\b|(?=\W)/) + } + shadowRef.current.innerHTML = units.current + .map(c => { + return `${c}` + }) + .join("") + const ellipsisIndex = putEllipsis(calcIndexes()) + const nextClamped = ellipsisIndex > -1 + const nextDisplayedText = nextClamped ? units.current.slice(0, ellipsisIndex).join("") : props.text + setClamped(nextClamped) + setDisplayedText(nextDisplayedText) + onReflow({ clamped: nextClamped, text: nextDisplayedText }) + } + + // return the index of the first letter/word of each line + // row count: maxLine + 1 + const calcIndexes = () => { + const indexes = [0] + let spanNode = shadowRef.current.firstElementChild + if (!spanNode) return indexes + + let index = 0 + let line = 1 + let offsetTop = spanNode.offsetTop + while ((spanNode = spanNode.nextElementSibling)) { + if (spanNode.offsetTop > offsetTop) { + line++ + indexes.push(index) + offsetTop = spanNode.offsetTop + } + index++ + if (line > maxLine) { + break + } + } + return indexes + } + + const putEllipsis = indexes => { + // no ellipsis + if (indexes.length <= maxLine) return -1 + const lastIndex = indexes[maxLine] + const truncatedUnits = units.current.slice(0, lastIndex) + + // the first letter/word of maxLine + 1 row + const maxOffsetTop = shadowRef.current.children[lastIndex].offsetTop + shadowRef.current.innerHTML = + truncatedUnits + .map((c, i) => { + return `${c}` + }) + .join("") + `${ellipsisRef.current.innerHTML}` + const ellipsisNode = shadowRef.current.lastElementChild + let lastTextNode = prevSibling(ellipsisNode, 1) + while ( + lastTextNode && + (ellipsisNode.offsetTop > maxOffsetTop || + ellipsisNode.offsetHeight > lastTextNode.offsetHeight || + ellipsisNode.offsetTop > lastTextNode.offsetTop) + ) { + shadowRef.current.removeChild(lastTextNode) + lastTextNode = prevSibling(ellipsisNode, 1) + truncatedUnits.pop() + } + return truncatedUnits.length + } + + return ( + <> + + {trimRight ? displayedText.trimRight() : displayedText} + {clamped && {ellipsis}} + +
+ + {ellipsis} + + + ) +} + +export default LinesEllipsis \ No newline at end of file diff --git a/src/components/Tooling/Content/AdditionalInfo/AdditionalInfo.module.css b/src/components/Tooling/Content/AdditionalInfo/AdditionalInfo.module.css new file mode 100644 index 000000000..b2dd66711 --- /dev/null +++ b/src/components/Tooling/Content/AdditionalInfo/AdditionalInfo.module.css @@ -0,0 +1,107 @@ +.additionalInfo { + height: 100%; + overflow: auto; + background: rgba(255, 255, 255, 0.5); + border-radius: 15px; + display: flex; + justify-content: center; + max-width: 532px; + margin: 0 auto; + position: relative; +} +.closeButton { + position: absolute; + right: 20px; + top: 20px; + cursor: pointer; + display: none; +} +.infoBox { + padding: 18px 20px; + width: 100%; +} + +.infoBox p { + font-size: 14px; +} + +.title { + font-size: 18px; + font-style: normal; + font-weight: 600; + line-height: normal; + letter-spacing: 0.18px; + margin-bottom: 15px; +} + +.content { + max-height: 460px; + overflow: auto; +} + +.noToolInfo { + text-align: center; + margin: 0 auto; + height: max-content; + max-width: 280px; + align-self: center; +} + +.noToolInfo svg { + margin-bottom: 20px; +} + +.noToolInfoTitle { + color: #ababab; + text-align: center; + font-size: 18px; + font-style: normal; + font-weight: 600; + line-height: normal; + letter-spacing: 0.18px; + margin-bottom: 8px; +} + +.noToolInfoDescription { + color: #ababab; + text-align: center; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 18px; /* 128.571% */ + letter-spacing: 0.14px; +} + +@media screen and (max-width: 1300px) { + .closeButton { + display: block; + } + .additionalContainer { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(16, 16, 16, 0.2); + z-index: 9999; + height: 100%; + max-height: 100%; + padding: 20px; + display: flex; + } + .additionalInfo { + background: #ffffff; + border-radius: 15px; + justify-self: center; + align-self: center; + width: 100%; + height: 532px; + } + + .noToolSelected { + display: none; + } + .noToolInfo { + padding-top: 20px; + } +} diff --git a/src/components/Tooling/Content/AdditionalInfo/AdditionalInfo.tsx b/src/components/Tooling/Content/AdditionalInfo/AdditionalInfo.tsx new file mode 100644 index 000000000..e03c8333f --- /dev/null +++ b/src/components/Tooling/Content/AdditionalInfo/AdditionalInfo.tsx @@ -0,0 +1,106 @@ +import styles from "./AdditionalInfo.module.css" +import { useState, useEffect, useMemo } from "preact/hooks" +import { clsx } from "~/lib" + +enum Status { + NO_ADDITIONAL_INFO, + HAS_ADDITIONAL_INFO, + NO_TOOL_SELECTED, +} + +const AdditionalInfo = ({ data, selectedTool, onClose }) => { + const status = useMemo(() => { + if (!selectedTool) { + return Status.NO_TOOL_SELECTED + } + + if (selectedTool && !selectedTool.remarkPluginFrontmatter.noAdditionalInfo) { + return Status.HAS_ADDITIONAL_INFO + } + + return Status.NO_ADDITIONAL_INFO + }, [selectedTool]) + + return ( + <> +
+
+
+ + + +
+ + {status === Status.HAS_ADDITIONAL_INFO && ( +
+
{selectedTool.remarkPluginFrontmatter.name}
+
{data}
+
+ )} + {status !== Status.HAS_ADDITIONAL_INFO && ( +
+ + + + + + + + + + + + + + + + + + + {status === Status.NO_TOOL_SELECTED ? ( + <> +
No tool selected
+
+ Try selecting a tool to learn more about using it on Scroll. +
+ + ) : ( + <> +
No additional information for this project
+
+ Try selecting another tool to learn more about using it on Scroll. +
+ + )} +
+ )} +
+
+ + ) +} + +export default AdditionalInfo diff --git a/src/components/Tooling/Content/Category/Category.module.css b/src/components/Tooling/Content/Category/Category.module.css new file mode 100644 index 000000000..6060d581c --- /dev/null +++ b/src/components/Tooling/Content/Category/Category.module.css @@ -0,0 +1,31 @@ +.toolsCategory { + display: flex; + flex-wrap: wrap; + padding-left: 0; +} +.toolsCategory .item { + display: inline-flex; + height: 44px; + padding: 0px 30px; + flex-direction: column; + justify-content: center; + align-items: center; + flex-shrink: 0; + border-radius: 100px; + margin-top: 0; + cursor: pointer; + margin-right: 13px; + margin-bottom: 10px; + border-width: 1px; + border-style: solid; +} +.toolsCategory .item.active { +} +@media screen and (max-width: 50em) { + .toolsCategory .item { + height: 36px; + padding: 0px 20px; + margin-right: 12px; + margin-bottom: 12px; + } +} diff --git a/src/components/Tooling/Content/Category/Category.tsx b/src/components/Tooling/Content/Category/Category.tsx new file mode 100644 index 000000000..4d61e7218 --- /dev/null +++ b/src/components/Tooling/Content/Category/Category.tsx @@ -0,0 +1,25 @@ +import { useState, useEffect, useMemo } from "preact/hooks" +import { clsx } from "~/lib" +import styles from "./Category.module.css" + +const Category = ({ categories, value, onChange }) => { + return ( + + ) +} + +export default Category diff --git a/src/components/Tooling/Content/Content.module.css b/src/components/Tooling/Content/Content.module.css new file mode 100644 index 000000000..d84b42e7b --- /dev/null +++ b/src/components/Tooling/Content/Content.module.css @@ -0,0 +1,24 @@ +.toolsContainer { + border-radius: 27px; + /* background: #fff8f3; */ + padding: 30px; + margin-bottom: 30px; +} + +.toolsList { + display: grid; + grid-template-columns: 1fr minmax(424px, 1fr); + /* grid-template-columns: 8fr 2fr; */ + gap: 30px; + margin-top: 30px; +} + +.toolsList > div { + overflow: scroll; +} + +@media screen and (max-width: 1300px) { + .toolsList { + grid-template-columns: 1fr; + } +} diff --git a/src/components/Tooling/Content/Content.tsx b/src/components/Tooling/Content/Content.tsx new file mode 100644 index 000000000..dc655f051 --- /dev/null +++ b/src/components/Tooling/Content/Content.tsx @@ -0,0 +1,94 @@ +import styles from "./Content.module.css" +import { clsx } from "~/lib" +import { useState, useEffect, useMemo } from "preact/hooks" +import SearchInput from "./Search/SearchInput" +import NetworkSelector from "./NetworkSelector/NetworkSelector" +import Category from "./Category/Category" +import List from "./List/List" +import AdditionalInfo from "./AdditionalInfo/AdditionalInfo" + +const Content = ({ tools, toolsMarkdown }) => { + const categories = [...new Set(tools.flatMap((tool) => tool.remarkPluginFrontmatter.category))] + + const [selectedTool, setSelectedTool] = useState(null) + + const [searchParams, setSearchParams] = useState({ + category: [], + network: "All networks", + keyword: "", + }) + + const handleChangeCategory = (value) => { + setSearchParams((prev) => { + const newCategory = prev.category.includes(value) + ? prev.category.filter((item) => item !== value) + : [...prev.category, value] + + return { + ...prev, + category: newCategory, + } + }) + } + + const handleChangeNetwork = (value) => { + setSearchParams((pre) => ({ + ...pre, + network: value, + })) + } + + const handleChangeKeyword = (e) => { + setSearchParams((prev) => ({ + ...prev, + keyword: e.target.value, + })) + } + + const handleToolClick = (blog) => { + setSelectedTool(blog) + } + + useEffect(() => { + if (selectedTool) { + document.querySelectorAll(".tools-item").forEach((div) => { + div.style.display = "none" + }) + const selectedDiv = document.getElementById(selectedTool.id) + if (selectedDiv) { + selectedDiv.style.display = "block" + } + } + }, [selectedTool]) + + const filteredTools = useMemo(() => { + // setSelectedTool(null) + return tools.filter((tool) => { + const categoryMatch = + searchParams.category.length === 0 || + searchParams.category.some((category) => tool.remarkPluginFrontmatter.category.includes(category)) + const networkMatch = + searchParams.network === "All networks" || tool.remarkPluginFrontmatter.network.includes(searchParams.network) + const keywordMatch = tool.remarkPluginFrontmatter.name.toLowerCase().includes(searchParams.keyword.toLowerCase()) + + return categoryMatch && networkMatch && keywordMatch + }) + }, [searchParams]) + + return ( +
+ +
+ + +
+ +
+ + handleToolClick(null)} data={toolsMarkdown} selectedTool={selectedTool} /> +
+
+ ) +} + +export default Content diff --git a/src/components/Tooling/Content/List/List.module.css b/src/components/Tooling/Content/List/List.module.css new file mode 100644 index 000000000..213f3243e --- /dev/null +++ b/src/components/Tooling/Content/List/List.module.css @@ -0,0 +1,62 @@ +.toolsContainerList { + border-radius: 15px; + height: 532px; + overflow: auto; +} +.toolItem { + display: flex; + padding: 20px; +} + +.toolItem:hover, +.toolItem.active { + background-color: #f9f9f9; + cursor: pointer; +} + +.toolItem:not(:last-child) { + border-bottom: 1px solid #ece8e3; +} + +.logo { + width: 66px; + height: fit-content; + flex-shrink: 0; + border-radius: 15px; + margin-right: 15px; + margin-top: 6px; +} + +.toolInfo { + width: 100%; +} + +.toolName { + color: #101010; + font-size: 18px; + font-style: normal; + font-weight: 600; + line-height: normal; + letter-spacing: 0.18px; + display: flex; + justify-items: center; + align-items: center; +} + +.category { + display: inline-block; + padding: 3px 5px; + font-size: 12px; + color: #0f8e7e; + letter-spacing: 0.12px; + border-radius: 5px; + background: #dffcf8; + margin-right: 5px; +} + +.toolDescription { + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: 18px; +} diff --git a/src/components/Tooling/Content/List/List.tsx b/src/components/Tooling/Content/List/List.tsx new file mode 100644 index 000000000..06a5b6335 --- /dev/null +++ b/src/components/Tooling/Content/List/List.tsx @@ -0,0 +1,104 @@ +import styles from "./List.module.css" +import LinesEllipsis from "../../../../components/LinesEllipsis" +import { clsx } from "~/lib" + +const List = ({ tools, onChange, selectedTool }) => { + const handleReflow = (value) => { + // don't trigger measure when the height exceeds the standard height by default + // if (!isExpended && cardRef.current!.clientHeight > standardHeight) { + // return + // } + // onResize() + } + + return ( +
+ {tools.map((tool, index) => ( +
onChange(tool)} + className={clsx( + styles.toolItem, + tool.id === selectedTool?.id && "bg-[#f9f9f9] dark:bg-black", + "hover:bg-[#f9f9f9] dark:hover:bg-black" + )} + > + {tool.remarkPluginFrontmatter.logo.alt} +
+
+
+ {tool.remarkPluginFrontmatter.name} + window.open(tool.remarkPluginFrontmatter.website, "_blank")} + > + + + + + + + + + +
+ + {!tool.remarkPluginFrontmatter.noAdditionalInfo && ( + + + + + + )} +
+
+ {tool.remarkPluginFrontmatter.category.map((category, index) => ( + + {category} + + ))} +
+ +  ... more + + } + basedOn="words" + onReflow={handleReflow} + /> +
+
+ ))} +
+ ) +} + +export default List diff --git a/src/components/Tooling/Content/NetworkSelector/NetworkSelector.module.css b/src/components/Tooling/Content/NetworkSelector/NetworkSelector.module.css new file mode 100644 index 000000000..573d68b87 --- /dev/null +++ b/src/components/Tooling/Content/NetworkSelector/NetworkSelector.module.css @@ -0,0 +1,56 @@ +.container { + height: 40px; + width: 218px; + position: relative; + z-index: 1; +} + +.networkSelector { + position: absolute; + left: 0; + top: 0; + width: 218px; + border-radius: 20px; + border: 1px solid #101010; + background: #fff; + display: flex; + justify-content: space-between; + padding: 0 12px; + align-items: flex-start; + cursor: pointer; + overflow: visible; +} + +.networkSelector .optionsList { + margin-bottom: 0; + padding-left: 0; + flex: 1; + margin-left: 7px; + color: #101010; + list-style: none; +} + +.optionsList li { + height: 38px; + line-height: 38px; + font-size: 16px; + font-weight: bold; + font-family: var(--font-family-body-text); + letter-spacing: 0.16px; +} + +@media (max-width: 50em) { + .container, + .networkSelector { + } + .optionsList { + margin-left: 0; + } +} + +@media (max-width: 604px) { + .container, + .networkSelector { + width: 148px; + } +} diff --git a/src/components/Tooling/Content/NetworkSelector/NetworkSelector.tsx b/src/components/Tooling/Content/NetworkSelector/NetworkSelector.tsx new file mode 100644 index 000000000..adf563d7a --- /dev/null +++ b/src/components/Tooling/Content/NetworkSelector/NetworkSelector.tsx @@ -0,0 +1,65 @@ +import { useState, useEffect, useMemo } from "preact/hooks" +import { clsx } from "~/lib" +import styles from "./NetworkSelector.module.css" + +const NetworkSelector = ({ value, onChange }) => { + const [isModalOpen, setIsModalOpen] = useState(false) + const networks = ["All networks", "Mainnet", "Testnet"] + + const handleNetworkClick = (network) => { + onChange(network) + setIsModalOpen(!isModalOpen) + } + + const filteredNetworks = useMemo(() => { + if (isModalOpen) { + return [value, ...networks.filter((network) => network !== value)] + } + return [value] + }, [isModalOpen, networks, value]) + + return ( +
+
+ + + +
    + {filteredNetworks.map((network, idx) => { + return ( +
  • handleNetworkClick(network)}> + {network} +
  • + ) + })} +
+ + + +
+
+ ) +} + +export default NetworkSelector diff --git a/src/components/Tooling/Content/Search/SearchInput.module.css b/src/components/Tooling/Content/Search/SearchInput.module.css new file mode 100644 index 000000000..e33b5efa2 --- /dev/null +++ b/src/components/Tooling/Content/Search/SearchInput.module.css @@ -0,0 +1,80 @@ +.wrapper { + display: flex; + align-items: center; + justify-content: center; + gap: 1rem; + width: 100%; + flex-shrink: 0; + overflow: hidden; + + flex: 1; + height: 40px; + border-radius: 20px; + padding: unset; + margin-right: 30px; +} + +.input { + margin: 0; + font-size: 16px; + width: 100%; + outline: none; + display: block; + appearance: none; + flex-grow: 1; + padding-left: 36px; + border-bottom: 1px solid #000; + border-radius: 0; + background: url(/assets/search.svg) left 12px top 50% / 17px no-repeat; + border: none; + height: 35px; + line-height: 35px; + padding-bottom: 0; + @apply text-black border-black; +} +.input.large { + height: 48px; + border-bottom: none; + padding-bottom: 0; + background: transparent url(/svgs/search.svg) left center no-repeat; + background-size: 20px auto; +} + +.input::placeholder { + font-style: italic; +} + +.input:focus { + outline: none; + border-radius: 0; +} + +.closeButton { + background-color: transparent; + border: none; + padding: 0; + display: none; + height: 25px; +} + +@media (max-width: 50em) { + .wrapper { + margin-right: 16px; + } + + .input { + @apply pl-[40px]; + } + + .input:focus { + @apply rounded-primary; + } + + .closeButton { + display: block; + } + + .closeButtonMobile { + display: none; + } +} diff --git a/src/components/Tooling/Content/Search/SearchInput.tsx b/src/components/Tooling/Content/Search/SearchInput.tsx new file mode 100644 index 000000000..3e6b20c91 --- /dev/null +++ b/src/components/Tooling/Content/Search/SearchInput.tsx @@ -0,0 +1,22 @@ +import React, { ChangeEvent, useCallback, useEffect, useState, useRef } from "react" +import styles from "./SearchInput.module.css" +import { clsx } from "~/lib" + +const SearchInput = ({ value, onChange }) => { + const inputRef = useRef(null) + + return ( +
+ +
+ ) +} + +export default SearchInput diff --git a/src/components/Tooling/Tooling.astro b/src/components/Tooling/Tooling.astro new file mode 100644 index 000000000..ffd5c22c3 --- /dev/null +++ b/src/components/Tooling/Tooling.astro @@ -0,0 +1,30 @@ +--- +import { getCollection } from "astro:content" +import Content from "./Content/Content.tsx" + +const pages = await getCollection("tools") + +const tools = await Promise.all( + pages.map(async (page) => { + const renderedPage = await page.render() + return { + ...renderedPage, + id: "tool-" + renderedPage.remarkPluginFrontmatter.name.toLowerCase().replace(/ /g, "-"), + } + }) +) +--- + + + + + { + tools.map((tool, index) => ( + + )) + } + + + diff --git a/src/config/sidebar.ts b/src/config/sidebar.ts index 4fcd504fc..1a5760f82 100644 --- a/src/config/sidebar.ts +++ b/src/config/sidebar.ts @@ -132,6 +132,10 @@ export const getSidebar = () => { // }, // ], }, + { + title: t("sidebar.developers.toolingDeployedOnScroll"), + url: formatUrl("developers/tooling-deployed-on-scroll"), + }, ], }, { diff --git a/src/content/config.ts b/src/content/config.ts index ce53f516c..c863de902 100644 --- a/src/content/config.ts +++ b/src/content/config.ts @@ -18,6 +18,7 @@ const docsCollection = defineCollection({ stub: z.string().optional(), ecosystem: z.string().optional(), l2healthflag: z.string().optional(), + widerContent: z.boolean().optional(), metadata: z .object({ @@ -31,6 +32,25 @@ const docsCollection = defineCollection({ }), }) +const toolsCollection = defineCollection({ + schema: z.object({ + name: z.string(), + excerpt: z.string(), + category: z.array(z.string()), + // network: z.string().optional(), + network: z.array(z.string()).optional(), + logo: z + .object({ + src: z.string().optional(), + alt: z.string().optional(), + }) + .optional(), + website: z.string().optional(), + noAdditionalInfo: z.boolean().optional(), + }), +}) + export const collections = { docs: docsCollection, + tools: toolsCollection, } diff --git a/src/content/docs/en/developers/scroll-contracts.mdx b/src/content/docs/en/developers/scroll-contracts.mdx index 5832db79d..c92c4e22a 100644 --- a/src/content/docs/en/developers/scroll-contracts.mdx +++ b/src/content/docs/en/developers/scroll-contracts.mdx @@ -81,59 +81,12 @@ Use the table below to configure your Ethereum tools to the Scroll mainnet. - WETH L2: [`0x5300000000000000000000000000000000000004`](https://scrollscan.com/address/0x5300000000000000000000000000000000000004) - Transaction Fee Vault: [`0x5300000000000000000000000000000000000005`](https://scrollscan.com/address/0x5300000000000000000000000000000000000005) -## Protocols on Scroll Mainnet - -### Uniswap v3 - -- Main Contracts - - Core Factory: [`0x70C62C8b8e801124A4Aa81ce07b637A3e83cb919`](https://scrollscan.com/address/0x70C62C8b8e801124A4Aa81ce07b637A3e83cb919) - - NFT Position Manager: [`0xB39002E4033b162fAc607fc3471E205FA2aE5967`](https://scrollscan.com/address/0xB39002E4033b162fAc607fc3471E205FA2aE5967) - - Router: [`0xfc30937f5cDe93Df8d48aCAF7e6f5D8D8A31F636`](https://scrollscan.com/address/0xfc30937f5cDe93Df8d48aCAF7e6f5D8D8A31F636) -- Additional Contracts - - multicall2Address: [`0xC1D2e074C38FdD5CA965000668420C80316F0915`](https://scrollscan.com/address/0xC1D2e074C38FdD5CA965000668420C80316F0915) - - proxyAdminAddress: [`0x1E6dcAb806A42055098f23E2B3ac72D6E195F967`](https://scrollscan.com/address/0x1E6dcAb806A42055098f23E2B3ac72D6E195F967) - - tickLensAddress: [`0x85780e12e90D2a684eB8E7404c985b5B5c8ce7E9`](https://scrollscan.com/address/0x85780e12e90D2a684eB8E7404c985b5B5c8ce7E9) - - nftDescriptorLibraryAddressV1_3_0: [`0xAeE9c206ba89F3DA25EEe4636208519e0B86965B`](https://scrollscan.com/address/0xAeE9c206ba89F3DA25EEe4636208519e0B86965B) - - nonfungibleTokenPositionDescriptorAddressV1_3_0: [`0xACcf12204b7591B2ECCEFe737440B0f53748B191`](https://scrollscan.com/address/0xACcf12204b7591B2ECCEFe737440B0f53748B191) - - descriptorProxyAddress: [`0x675DD953225D296A44790dC1390a1E7eF378f464`](https://scrollscan.com/address/0x675DD953225D296A44790dC1390a1E7eF378f464) - - v3MigratorAddress: [`0xF00577B5Dd0DA227298E954Ed11356F264Cf93d4`](https://scrollscan.com/address/0xF00577B5Dd0DA227298E954Ed11356F264Cf93d4) - - v3StakerAddress: [`0xFdFbE973c9ecB036Ecfb7af697FcACe789D3f928`](https://scrollscan.com/address/0xFdFbE973c9ecB036Ecfb7af697FcACe789D3f928) - - quoterV2Address: [`0x2566e082Cb1656d22BCbe5644F5b997D194b5299`](https://scrollscan.com/address/0x2566e082Cb1656d22BCbe5644F5b997D194b5299) - -### Safe - -Scroll is available in the [official Safe app](app.safe.global), and the transaction service API is at https://safe-transaction-scroll.safe.global/. - - - - -- CompatibilityFallbackHandler: [`0xf48f2B2d2a534e402487b3ee7C18c33Aec0Fe5e4`](https://scrollscan.com/address/0xf48f2B2d2a534e402487b3ee7C18c33Aec0Fe5e4) -- CreateCall: [`0x7cbB62EaA69F79e6873cD1ecB2392971036cFAa4`](https://scrollscan.com/address/0x7cbB62EaA69F79e6873cD1ecB2392971036cFAa4) -- DefaultCallbackHandler: [`0x1AC114C2099aFAf5261731655Dc6c306bFcd4Dbd`](https://scrollscan.com/address/0x1AC114C2099aFAf5261731655Dc6c306bFcd4Dbd) -- GnosisSafe: [`0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552`](https://scrollscan.com/address/0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552) -- GnosisSafeL2: [`0x3E5c63644E683549055b9Be8653de26E0B4CD36E`](https://scrollscan.com/address/0x3E5c63644E683549055b9Be8653de26E0B4CD36E) -- GnosisSafeProxyFactory: [`0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2`](https://scrollscan.com/address/0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2) -- MultiSend: [`0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761`](https://scrollscan.com/address/0xA238CBeb142c10Ef7Ad8442C6D1f9E89e07e7761) -- MultiSendCallOnly: [`0x40A2aCCbd92BCA938b02010E17A5b8929b49130D`](https://scrollscan.com/address/0x40A2aCCbd92BCA938b02010E17A5b8929b49130D) -- SignMessageLib: [`0xA65387F16B013cf2Af4605Ad8aA5ec25a2cbA3a2`](https://scrollscan.com/address/0xA65387F16B013cf2Af4605Ad8aA5ec25a2cbA3a2) -- SimulateTxAccessor: [`0x59AD6735bCd8152B84860Cb256dD9e96b85F69Da`](https://scrollscan.com/address/0x59AD6735bCd8152B84860Cb256dD9e96b85F69Da) -#### Ethereum Attestation Service (EAS) - -- EAS: [`0xC47300428b6AD2c7D03BB76D05A176058b47E6B0`](https://scrollscan.com/address/0xC47300428b6AD2c7D03BB76D05A176058b47E6B0) -- SchemaRegistry: [`0xD2CDF46556543316e7D34e8eDc4624e2bB95e3B6`](https://scrollscan.com/address/0xD2CDF46556543316e7D34e8eDc4624e2bB95e3B6) -- EIP712Proxy: [`0x77b7DA1c40762Cd8AFfE2069b575328EfD4D9801`](https://scrollscan.com/address/0x77b7DA1c40762Cd8AFfE2069b575328EfD4D9801) -- Indexer: `Not deployed yet` - -## Additional Useful Contracts +### Additional Useful Contracts - Multicall3: [`0xcA11bde05977b3631167028862bE2a173976CA11`](https://scrollscan.com/address/0xcA11bde05977b3631167028862bE2a173976CA11) -## Tokens +### Tokens